Spring 注解

mac2024-05-30  38

文章目录

1、`@Configuration` 和 `@Bean`1.1、`@Configuration` 配置类注解1.2、`@Bean` 添加bean注解 2、`@ComponentScan` 组件扫描注解2.1、@ComponentScan参数说明:2.2、扫描时,排除 Controller 注解的bean2.3、只包含 Controller注解的bean和 BookService类型的bean2.4、自定义 TypeFilter(规则扫描) 3、`@Scope` 作用域3.1、`@Scope`的取值3.2、`@Scope`示例 4、 `@Lazy` 懒加载5、`@Conditional()`5.1、`@Conditional()` 作用于 方法上5.2、`@Conditional()` 作用于 类上 给容器中注册组件;6、`@Import` 批量导入组件6.1、`@Import` 导入一个组件6.2、`@Import` 导入多个组件6.3、`@Import` 使用 ImportSelector 导入组件6.4、`@Import` 使用 `ImportBeanDefinitionRegistrar` 导入组件 7、使用 FactoryBean 注册组件8、 `@Bean`8.1、@Bean指定 初始化和销毁方法8.1.1 `@Bean` 多实例 8.2、InitializingBean, DisposableBean 接口8.3 `@PostConstruct` ,`@PreDestroy`8.4、 BeanPostProcessor bean的后置处理器 9、`@PropertySource`、`@ConfigurationProperties`9.1、`@PropertySource`的使用位置 10、`@Value`11、`@Autowired`、`@Qualifier()` 自动注入11.1、`@Autowired`11.1.1、示例111.1.2 示例2 `@Qualifier` 指定装配的注解 12、 @Primary 首选、优先装配的注解12.1、示例1 装 bookDao3 的 bean12.2、示例1 @Qualifier、@Primary 13、@Resource14、 @Inject

1、@Configuration 和 @Bean

public class Person { private String name; private Integer age; private String nickName; public Person() { super(); } public Person(String name, Integer age) { super(); this.name = name; this.age = age; } getter/setter..... @Override public String toString() { return "Person [name=" + name + ", age=" + age + ", nickName=" + nickName + "]"; } } import com.atguigu.bean.Person; //配置类==配置文件 @Configuration //告诉Spring这是一个配置类 public class MainConfig { //给容器中注册一个Bean;类型为返回值的类型,id默认是用方法名作为id @Bean("person") public Person person01(){ return new Person("lisi", 20); } }

1.1、@Configuration 配置类注解

@Configuration 修改的类就是配置类,其作于相当于 applicationContent.xml 配置文件 。

applicationContent.xml:

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> </beans>

1.2、@Bean 添加bean注解

@Bean修改的方法,就是给容器中注册一个Bean 。等价于 applicationContent.xml中的 <bean> 标签中的内容:

<bean id="person" class="com.atguigu.bean.Person" scope="prototype" > <property name="age" value="${}"></property> <property name="name" value="zhangsan"></property> </bean>

@Bean("person") 中的 person 等价于 <bean> 标签的id的值。 @Bean 中没有给出值,id默认就是方法名。

2、@ComponentScan 组件扫描注解

@ComponentScan 等价于 <context:component-scan base-package="xxx.xxx" ></context:component-scan>

2.1、@ComponentScan参数说明:

value : 扫描的包路径。value 是数组类型的,可以指定多个路径。

excludeFilters = Filter[] :排除组件

includeFilters = Filter[] :只包含哪些组件

useDefaultFilters 运行默认过虑规则(默认是true,扫描所有的),如果 使用 includeFilters 时,一定要将 useDefaultFilters 设置为false。

FilterType.ANNOTATION:按照注解FilterType.ASSIGNABLE_TYPE:按照给定的类型;FilterType.ASPECTJ:使用ASPECTJ表达式FilterType.REGEX:使用正则指定FilterType.CUSTOM:使用自定义规则

2.2、扫描时,排除 Controller 注解的bean

excludeFilters的用法。

@Configuration @ComponentScan(value="com.atguigu",excludeFilters = { @Filter(type=FilterType.ANNOTATION,classes={Controller.class}) /*@Filter(type=FilterType.ANNOTATION,classes={Controller.class,Service.class})*/ }) public class MainConfig { //给容器中注册一个Bean;类型为返回值的类型,id默认是用方法名作为id @Bean("person") public Person person01(){ return new Person("lisi", 20); } }

添加多个排除示例 : 如,排除 Controller和Service 注解的bean

@Configuration @ComponentScan(value="com.atguigu",excludeFilters = { @Filter(type=FilterType.ANNOTATION,classes={Controller.class, Service.class}) })

也可以分开写:

@Configuration @ComponentScan(value="com.atguigu",excludeFilters = { @Filter(type=FilterType.ANNOTATION,classes={Controller.class}), @Filter(type=FilterType.ANNOTATION,classes={Service.class}) })

2.3、只包含 Controller注解的bean和 BookService类型的bean

includeFilters的用法:

使用includeFilters时,一定要设置 useDefaultFilters = false 。

@Configuration @ComponentScan(value="com.atguigu",includeFilters = { @Filter(type=FilterType.ANNOTATION,classes={Controller.class}), @Filter(type=FilterType.ASSIGNABLE_TYPE,classes={BookService.class}) },useDefaultFilters = false) public class MainConfig { //给容器中注册一个Bean;类型为返回值的类型,id默认是用方法名作为id @Bean("person") public Person person01(){ return new Person("lisi", 20); } }

2.4、自定义 TypeFilter(规则扫描)

//配置类==配置文件 @Configuration //告诉Spring这是一个配置类 @ComponentScan(value="com.atguigu",includeFilters = { @Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class}) },useDefaultFilters = false) public class MainConfig { //给容器中注册一个Bean;类型为返回值的类型,id默认是用方法名作为id @Bean("person") public Person person01(){ return new Person("lisi", 20); } }

MyTypeFilter 的规则是 只要 className 中含有er的,就被过滤。

public class MyTypeFilter implements TypeFilter { /** * metadataReader:读取到的当前正在扫描的类的信息 * metadataReaderFactory:可以获取到其他任何类信息的 */ @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { //获取当前类注解的信息 AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata(); //获取当前正在扫描的类的类信息 ClassMetadata classMetadata = metadataReader.getClassMetadata(); //获取当前类资源(类的路径) Resource resource = metadataReader.getResource(); String className = classMetadata.getClassName(); System.out.println("--->"+className); if(className.contains("er")){ return true; } return false; } }

3、@Scope 作用域

@Scope 对bean组件的作用域修饰,所以 @Bean 用在同一地方。

3.1、@Scope的取值

取值是否默认说明singleton单实例的(默认值)ioc容器启动会创建对象放到ioc容器中。以后每次获取就是直接从容器( map.get() )中拿,prototype多实例的:ioc容器启动并不会去创建对象放在容器中。每次获取的时候才会调用方法创建对象;request-同一次请求创建一个实例session-同一个session创建一个实例

3.2、@Scope示例

@Configuration public class MainConfig2 { @Scope("prototype") @Bean("person") public Person person(){ System.out.println("给容器中添加Person...."); return new Person("张三", 25); } }

上面的代码等价于

<bean id="person" class="com.atguigu.bean.Person" scope="prototype" > <property name="name" value="张三"></property> <property name="age" value="25"></property> </bean>

4、 @Lazy 懒加载

@Lazy 只针对 单实例bean。

单实例bean 默认在容器启动的时候创建对象。 使用懒加载后,容器启动不创建对象,第一次使用(获取)Bean创建对象,并初始化;

@Configuration public class MainConfig2 { @Lazy @Bean("person") public Person person(){ System.out.println("给容器中添加Person...."); return new Person("张三", 25); } }

5、@Conditional()

按照一定的条件进行判断,满足条件给容器中注册bean 。

|作用域|说明|备注| |–|--| |方法上| 表示满足条件,将此bean 注册到容器中 |一般要配合@Bean使用| |类上| 表示满足条件,这个类中配置的所有bean 才有可能 注册到容器 |一般要配合 @Configuration 使用|

5.1、@Conditional() 作用于 方法上

LinuxCondition :

//判断是否linux系统 public class LinuxCondition implements Condition { /** * ConditionContext:判断条件能使用的上下文(环境) * AnnotatedTypeMetadata:注释信息 */ @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { // TODO是否linux系统 //1、能获取到ioc使用的beanfactory ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); //2、获取类加载器 ClassLoader classLoader = context.getClassLoader(); //3、获取当前环境信息 Environment environment = context.getEnvironment(); //4、获取到bean定义的注册类 BeanDefinitionRegistry registry = context.getRegistry(); String property = environment.getProperty("os.name"); //可以判断容器中的bean注册情况,也可以给容器中注册bean boolean definition = registry.containsBeanDefinition("person"); if(property.contains("linux")){ return true; } return false; } }

**WindowsCondition : **

//判断是否windows系统 public class WindowsCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { Environment environment = context.getEnvironment(); String property = environment.getProperty("os.name"); if(property.contains("Windows")){ return true; } return false; } }

**MainConfig2 : **

@Conditional({WindowsCondition.class}) : 当 WindowsCondition 满足条件时,就是向容器中注册 id=bill 的 bean 。

@Conditional(LinuxCondition.class): 当 LinuxCondition 满足条件时,就是向容器中注册 id=linus 的 bean 。

本示例比较特殊,最多只能满足一个bean注册到容器中。 实际使用 @Conditional() 中 ,只要满足条件,都可以的将bean注册到容器中。

@Configuration public class MainConfig2 { @Bean() public Person person(){ return new Person("zhangsan",25); } /** * @Conditional({Condition}) : 按照一定的条件进行判断,满足条件给容器中注册bean * * 如果系统是windows,给容器中注册("bill") * 如果是linux系统,给容器中注册("linus") */ @Conditional({WindowsCondition.class}) @Bean("bill") public Person person01(){ return new Person("Bill Gates",62); } @Conditional(LinuxCondition.class) @Bean("linus") public Person person02(){ return new Person("linus", 48); } }

测试类 :

public class IOCTest { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class); @Test public void test03(){ String[] namesForType = applicationContext.getBeanNamesForType(Person.class); ConfigurableEnvironment environment = applicationContext.getEnvironment(); //动态获取环境变量的值;Windows 10 String property = environment.getProperty("os.name"); System.out.println(property); for (String name : namesForType) { System.out.println(name); } Map<String, Person> persons = applicationContext.getBeansOfType(Person.class); System.out.println(persons); } }

测试时,可以通过 VM 参数 来测试

-Dos.name=linux

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NK39mhQC-1572512417389)(https://i.imgur.com/e288nd3.png)]

通过测试可以说明,当os.name 是 window 时,会向容器注册 id=bean 的 bean ; 当os.name 是 linux 时,会向容器注册 id=linus 的bean;

5.2、@Conditional() 作用于 类上

**MainConfig2 : **

@Conditional({WindowsCondition.class}) @Configuration public class MainConfig2 { @Bean() public Person person(){ return new Person("zhangsan",25); } /** * @Conditional({Condition}) : 按照一定的条件进行判断,满足条件给容器中注册bean * * 如果系统是windows,给容器中注册("bill") * 如果是linux系统,给容器中注册("linus") */ @Bean("bill") public Person person01(){ return new Person("Bill Gates",62); } @Conditional(LinuxCondition.class) @Bean("linus") public Person person02(){ return new Person("linus", 48); } }

解读: @Conditional({WindowsCondition.class}) 作用于类上,因此只有 os.name= windows 时,MainConfig2类 才会被加载。 在加载 MainConfig2类 中bean时,person(),person01() 可以被顺利注册到bean容器中, 而 person02() 要满足 @Conditional(LinuxCondition.class),即 os.name= linux ,显示不满足,所以无法被注册到容器中 。

给容器中注册组件;

1)、包扫描+组件标注注解(@Controller/@Service/@Repository/@Component)[自己写的类] 2)、@Bean[导入的第三方包里面的组件] , 缺点是一个一个注册,太麻烦了。 3)、@Import[快速给容器中导入一个组件] 1)、@Import(要导入到容器中的组件);容器中就会自动注册这个组件,id默认是全类名 2)、ImportSelector:返回需要导入的组件的全类名数组; 3)、ImportBeanDefinitionRegistrar:手动注册bean到容器中 4)、使用Spring提供的 FactoryBean(工厂Bean); 1)、默认获取到的是工厂bean调用getObject创建的对象 2)、要获取工厂Bean本身,我们需要给id前面加一个& &colorFactoryBean

6、@Import 批量导入组件

@Import 使用说明: 参数是数组类型的,可以导入多个组件; id 默认是 组件的全类名 。

6.1、@Import 导入一个组件

package com.atguigu.bean; public class Color { }

@Import(Color.class) 注解 表示 将 Color.class 导入到容器中 。 导入到容器的的id是 com.atguigu.bean.Color 。

@Configuration @Import(Color.class) public class MainConfig2 { }

测试:

public class IOCTest { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class); @Test public void testImport(){ printBeans(applicationContext); } private void printBeans(AnnotationConfigApplicationContext applicationContext){ String[] definitionNames = applicationContext.getBeanDefinitionNames(); for (String name : definitionNames) { System.out.println(name); } } }

打印结果是

mainConfig2 com.atguigu.bean.Color

6.2、@Import 导入多个组件

定义多个bean:

package com.atguigu.bean; public class Color { } package com.atguigu.bean; public class Red { }

@Import({Color.class,Red.class}) 注解 表示 将 Color,Red 导入到容器中 。

@Configuration @Import({Color.class,Red.class}) public class MainConfig2 { }

6.3、@Import 使用 ImportSelector 导入组件

MyImportSelector.java 自定义逻辑返回需要导入的组件

参数 AnnotationMetadata: 当前标注@Import注解的类的所有注解信息返回值,就是到导入到容器中的组件全类名 public class MyImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { //方法不要返回null值 return new String[]{"com.atguigu.bean.Blue","com.atguigu.bean.Yellow"}; } }

定义bean:

package com.atguigu.bean; public class Blue { } package com.atguigu.bean; public class Yellow { }

@Import({MyImportSelector.class}) 将 MyImportSelector 类返回的组件导入到容器中。

@Configuration @Import({Color.class,Red.class,MyImportSelector.class}) public class MainConfig2 { @Bean("person") public Person person(){ System.out.println("给容器中添加Person...."); return new Person("张三", 25); } }

测试(略)

6.4、@Import 使用 ImportBeanDefinitionRegistrar 导入组件

**MyImportBeanDefinitionRegistrar.java ** 如果有 Red 和 Blue 时,将 RainBow 导入到容器中

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { /** * AnnotationMetadata:当前类的注解信息 * BeanDefinitionRegistry : BeanDefinition注册类; * 把所有需要添加到容器中的bean; * 调用 BeanDefinitionRegistry.registerBeanDefinition手工注册进来 */ @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { boolean definition = registry.containsBeanDefinition("com.atguigu.bean.Red"); boolean definition2 = registry.containsBeanDefinition("com.atguigu.bean.Blue"); if(definition && definition2){ //指定Bean定义信息;(Bean的类型,Bean。。。) RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class); //注册一个Bean,指定bean名 registry.registerBeanDefinition("rainBow", beanDefinition); } } } package com.atguigu.bean; public class RainBow { }

MainConfig2 配置类:

@Import({MyImportBeanDefinitionRegistrar.class}) 注解表示 在 MyImportBeanDefinitionRegistrar 中注册的类,添加到容器中。

@Configuration @Import({Color.class,Red.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class}) public class MainConfig2 { @Bean("person") public Person person(){ System.out.println("给容器中添加Person...."); return new Person("张三", 25); } }

7、使用 FactoryBean 注册组件

//创建一个Spring定义的FactoryBean public class ColorFactoryBean implements FactoryBean<Color> { //返回一个Color对象,这个对象会添加到容器中 @Override public Color getObject() throws Exception { System.out.println("ColorFactoryBean...getObject..."); return new Color(); } @Override public Class<?> getObjectType() { return Color.class; } //是单例? //true:这个bean是单实例,在容器中保存一份 //false:多实例,每次获取都会创建一个新的bean; @Override public boolean isSingleton() { return false; } }

8、 @Bean

bean的生命周期: bean创建 --> 初始化 —> 销毁的过程

容器管理bean的生命周期: 我们可以自定义初始化和销毁方法;容器在bean进行到当前生命周期的时候来调用我们自定义的初始化和销毁方法

构造(对象创建)

单实例:在容器启动的时候创建对象多实例:在每次获取的时候创建对象

8.1、@Bean指定 初始化和销毁方法

配置类中注册bean到容器中,

@Bean 中没有给出值,id默认就是方法名。

initMethod 定义 初始化方法 destroyMethod 定义 销毁方法

@Configuration public class MainConfigOfLifeCycle { @Bean(initMethod="myInit",destroyMethod="myDetory") public Pig pig(){ System.out.println("MainConfigOfLifeCycle Pig @Bean"); return new Pig(); } }

pig()方法 等价于 下面的 xml 配置内容

<bean id="person" class="com.atguigu.bean.Pig" init-method="myInit" destroy-method="myDetory" > </bean>

定义bean —— Pig.java

@Component public class Pig { public Pig(){ System.out.println("Pig constructor..."); } public void myInit(){ System.out.println("Pig myInit() ------- @Bean(initMethod=\"myInit\") "); } public void myDetory(){ System.out.println("Pig myDetory() ------ @Bean(destroyMethod=\"myDetory\")"); } }

测试:

public class IOCTest_LifeCycle { @Test public void test01(){ //1、创建ioc容器 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class); System.out.println("容器创建完成..."); //applicationContext.getBean("pig"); //关闭容器 //applicationContext.close(); } }

打印:

MainConfigOfLifeCycle Pig @Bean Pig constructor... Pig myInit() ------- @Bean(initMethod="myInit") 容器创建完成... Pig myDetory() ------ @Bean(destroyMethod="myDetory")

打印结果解读:

Spring容器加载时,创建bean,先执行构造方法,打印 Pig constructor..., 再执行初始化方法,打印 Pig myInit() ------- @Bean(initMethod="myInit") 当程序执行关闭容器 ,即 执行 applicationContext.close();时, bean 执行销毁方法,打印 Pig myDetory() ------ @Bean(destroyMethod="myDetory") 。

假如不执行关闭容器, 即applicationContext.close(); 代码注释掉 , 则 destroyMethod() 不会执行, Pig myDetory() ------ @Bean(destroyMethod="myDetory")也不会打印。

8.1.1 @Bean 多实例

@Configuration public class MainConfigOfLifeCycle { @Scope("prototype") @Bean(initMethod="myInit",destroyMethod="myDetory") public Pig pig(){ System.out.println("MainConfigOfLifeCycle Pig @Bean"); return new Pig(); } }

执行测试,打印结果是

容器创建完成...

解读:

如果是多实例,

Spring容器加载时,不会初始化这个bean;在使用这个bean时 (比如,执行 applicationContext.getBean("pig");),才会创建bean;Spring 容器关闭时,也不会调用销毁方法;

8.2、InitializingBean, DisposableBean 接口

InitializingBean 接口 表示 初始化bean 接口,需要重写 afterPropertiesSet() 表示 所有属性(Properties)赋值(set)完毕后(after)的方法 。

DisposableBean 接口 表示 销毁bean 接口,需要重写 destroy() 。

public class Dog implements InitializingBean,DisposableBean { public Dog(){ System.out.println("Dog constructor..."); } @Override public void afterPropertiesSet() throws Exception { System.out.println("Dog InitializingBean.afterPropertiesSet() "); } @Override public void destroy() throws Exception { System.out.println("Dog DisposableBean.destroy() "); } }

配置类,将 Pig 注入到Spring 的Bean容器中。

@Configuration public class MainConfigOfLifeCycle { @Bean public Dog dog(){ return new Dog(); } }

测试类:

public class IOCTest_LifeCycle { @Test public void test01(){ //1、创建ioc容器 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class); System.out.println("容器创建完成..."); printBeans(applicationContext); applicationContext.getBean("dog"); //关闭容器 applicationContext.close(); } private void printBeans(AnnotationConfigApplicationContext applicationContext){ String[] definitionNames = applicationContext.getBeanDefinitionNames(); for (String name : definitionNames) { System.out.println(name); } } }

运行结果:

Dog constructor... Dog InitializingBean.afterPropertiesSet() 容器创建完成... org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.annotation.internalRequiredAnnotationProcessor org.springframework.context.annotation.internalCommonAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory mainConfigOfLifeCycle dog Dog DisposableBean.destroy()

8.3 @PostConstruct ,@PreDestroy

可以使用JSR250; @PostConstruct:在bean创建完成并且属性赋值完成, 来执行初始化方法。 等价于 InitializingBean 接口 。 @PreDestroy:在容器销毁bean之前, 通知我们进行清理工作 。等价于 DisposableBean 接口 。

public class Dog implements InitializingBean, DisposableBean { public Dog() { System.out.println("dog constructor..."); } @PostConstruct public void myPostConstruct() { System.out.println("Dog @PostConstruct 注解"); } @Override public void afterPropertiesSet() throws Exception { System.out.println("Dog InitializingBean.afterPropertiesSet() "); } // 容器移除对象之前 @PreDestroy public void detory() { System.out.println("Dog @PreDestroy注解"); } @Override public void destroy() throws Exception { System.out.println("Dog DisposableBean.destroy() "); } }

MainConfigOfLifeCycle ,IOCTest_LifeCycle 参考 8.2 。

运行结果:

dog constructor... Dog @PostConstruct 注解 Dog InitializingBean.afterPropertiesSet() 容器创建完成... org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.annotation.internalRequiredAnnotationProcessor org.springframework.context.annotation.internalCommonAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory mainConfigOfLifeCycle dog Dog @PreDestroy注解 Dog DisposableBean.destroy()

8.4、 BeanPostProcessor bean的后置处理器

在bean 构造、依赖注入完毕,在调用显示的初始化之前完成一些定制的初始化任务,重写 postProcessBeforeInitialization() 在bean初始化后,完成一些工作,重写 postProcessAfterInitialization()

@Component public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println(beanName+" BeanPostProcessor.postProcess_BeforeInitialization()"); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println(beanName+" BeanPostProcessor.postProcess_AfterInitialization()"+"\n"); return bean; } }

配置类:

@Configuration public class MainConfigOfLifeCycle { @Bean public Dog dog(){ return new Dog(); } @Bean public MyBeanPostProcessor myBeanPostProcessor(){ return new MyBeanPostProcessor(); } }

测试:

public class IOCTest_LifeCycle { @Test public void test01(){ //1、创建ioc容器 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class); System.out.println("容器创建完成..."); printBeans(applicationContext); //applicationContext.getBean("dog"); //关闭容器 applicationContext.close(); } private void printBeans(AnnotationConfigApplicationContext applicationContext){ String[] definitionNames = applicationContext.getBeanDefinitionNames(); for (String name : definitionNames) { System.out.println(name); } } }

运行结果:

org.springframework.context.event.internalEventListenerProcessor BeanPostProcessor.postProcess_BeforeInitialization() org.springframework.context.event.internalEventListenerProcessor BeanPostProcessor.postProcess_AfterInitialization() org.springframework.context.event.internalEventListenerFactory BeanPostProcessor.postProcess_BeforeInitialization() org.springframework.context.event.internalEventListenerFactory BeanPostProcessor.postProcess_AfterInitialization() dog constructor... dog BeanPostProcessor.postProcess_BeforeInitialization() Dog @PostConstruct 注解 Dog InitializingBean.afterPropertiesSet() dog BeanPostProcessor.postProcess_AfterInitialization() 容器创建完成... org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.annotation.internalRequiredAnnotationProcessor org.springframework.context.annotation.internalCommonAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory mainConfigOfLifeCycle dog myBeanPostProcessor Dog @PreDestroy注解 Dog DisposableBean.destroy()

运行结果 与 8.3 差别很大。 在每个bean的创建前和后 都执行了 postProcess_BeforeInitialization() 和 postProcess_AfterInitialization() 。

9、@PropertySource、@ConfigurationProperties

@PropertySource 加载 properties配置文件存储到Spring的 Environment。 使用: 加载完后,可以在任意的spring组件中使用${}取值

@PropertySource 加载 properties文件,@ConfigurationProperties 指定具体的值

@PropertySource(value = { "classpath:/person.properties" })

等价于下面的xml配置 :

?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd"> <context:property-placeholder location="classpath:person.properties" /> </beans>

9.1、@PropertySource的使用位置

只能用于被spring 扫描的组件上面

@PropertySource(value = { "classpath:/person.properties" }) @Configuration @ConfigurationProperties(prefix="person") public class MainConfigOfPropertyValues { @Bean public Person person() { return new Person(); } }

10、@Value

1、基本数值 2、可以写SpEL; #{} 3、可以写${};取出配置文件【properties】中的值(在运行环境变量里面的值)

public class Person { @Value("张三") // 1、基本数值 private String name; @Value("#{20-2}") //2、可以写SpEL; #{} private Integer age; @Value("${person.nickName}") 3、取出配置文件【properties】中的值 private String nickName; public Person() { super(); } public Person(String name, Integer age) { super(); this.name = name; this.age = age; } getter/setter... }

MainConfigOfPropertyValues的两个作用: 1、读取 person.properties 文件到Spring 环境变量中 2、将 Person 初始化到 Spring 的 Bean 容器中

//使用@PropertySource读取外部配置文件中的k/v保存到运行的环境变量中;加载完外部的配置文件以后使用${}取出配置文件的值 @PropertySource(value = { "classpath:/person.properties" }) @Configuration public class MainConfigOfPropertyValues { @Bean public Person person() { return new Person(); } }

person.properties:

person.nickName=\u5C0F\u674E\u56DB

测试类:

public class IOCTest_PropertyValue { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfPropertyValues.class); @Test public void test01(){ printBeans(applicationContext); System.out.println("================="); Person person = (Person) applicationContext.getBean("person"); System.out.println(person); } private void printBeans(AnnotationConfigApplicationContext applicationContext){ String[] definitionNames = applicationContext.getBeanDefinitionNames(); for (String name : definitionNames) { System.out.println(name); } } }

运行结果:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.annotation.internalRequiredAnnotationProcessor org.springframework.context.annotation.internalCommonAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory mainConfigOfPropertyValues person ================= Person [name=张三, age=18, nickName=小李四]

11、@Autowired、@Qualifier() 自动注入

11.1、@Autowired

@Autowired 即 @Autowired(required=true),自动注入失败,则抛异常。

@Autowired(required=true) 自动注入规则:

首先在容器中查询对应类型的bean,

如果查询的结果 为空 ,那么会抛出异常。解决方法,使用required=false ,不会抛出异常。如果查询结果刚好为 一个 ,就将该bean装配给 @Autowired 指定的数据。如果查询的结果 不止一个 ,那么 @Autowired 会根据 属性名 来查找;如果无法匹配,则抛异常。解决方案:设置 @Autowired(required=false)、增加@Qualifier("bookDao3") 。@Autowired 和 @Qualifier() 配合指定 beanName 的b ean 进行装配。

11.1.1、示例1

BookDao.java

//@Repository public class BookDao { private String lable = "1"; public String getLable() { return lable; } public void setLable(String lable) { this.lable = lable; } }

BookService.java :

@Service public class BookService { @Autowired private BookDao bookDao; }

配置类:

@Configuration @ComponentScan({"com.atguigu.service","com.atguigu.dao", "com.atguigu.controller","com.atguigu.bean"}) public class MainConifgOfAutowired { //@Bean("bookDao2") public BookDao bookDao2(){ BookDao bookDao = new BookDao(); bookDao.setLable("2"); return bookDao; } }

测试类:

public class IOCTest_Autowired { @Test public void test01(){ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConifgOfAutowired.class); printBeans(applicationContext); BookService bookService = applicationContext.getBean(BookService.class); System.out.println(bookService); } }

将 BookDao.java 类上的@Repository 注释掉 , 将 MainConifgOfAutowired.java 中 bookDao2() 的@Bean("bookDao2") 注释掉 , 再次运行,抛出如下异常:

警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'bookService': Unsatisfied dependency expressed through field 'bookDao'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.atguigu.dao.BookDao' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

原因 上面的第1条。

解决方案是

设置 @Autowired(required=false),即 自动注入不是必须的 。

11.1.2 示例2 @Qualifier 指定装配的注解

@Qualifier 表示 指定 具体 的 beanName 进行装配。

BookDao.java

@Repository public class BookDao { private String lable = "1"; public String getLable() { return lable; } public void setLable(String lable) { this.lable = lable; } }

BookService.java :

装配一个不存在的 bookDao3。

@Service public class BookService { @Autowired private BookDao bookDao3; }

配置类:

@Configuration @ComponentScan({"com.atguigu.service","com.atguigu.dao", "com.atguigu.controller","com.atguigu.bean"}) public class MainConifgOfAutowired { @Bean("bookDao2") public BookDao bookDao2(){ BookDao bookDao = new BookDao(); bookDao.setLable("2"); return bookDao; } }

测试类:

public class IOCTest_Autowired { @Test public void test01(){ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConifgOfAutowired.class); printBeans(applicationContext); BookService bookService = applicationContext.getBean(BookService.class); System.out.println(bookService); } }

装配一个不存在的 bookDao3,运行结果抛出了异常,原因是上面的第3条。

警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'bookService': Unsatisfied dependency expressed through field 'bookDao3'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.atguigu.dao.BookDao' available: expected single matching bean but found 2: bookDao,bookDao2

解决方案

如果想即使注入失败,也不想抛出异常的,是设置 @Autowired(required=false)、增加@Qualifier("bookDao3") 注解 :

@Service public class BookService { @Qualifier("bookDao3") @Autowired(required=false) private BookDao bookDao3; }

当前只有 bookDao,bookDao2, 当@Qualifier("bookDao3") 时,也不会报错。

也可以使用 @Qualifier 明确指定装配 bookDao, bookDao2 中的哪个bean 。

12、 @Primary 首选、优先装配的注解

@Primary:让 Spring 进行自动装配(@Autowired)的时候,默认使用首选的 bean; 也可以继续使用 @Qualifier 指定需要装配的 bean 的名字 。

12.1、示例1 装 bookDao3 的 bean

BookDao.java

@Repository public class BookDao { private String lable = "1"; public String getLable() { return lable; } public void setLable(String lable) { this.lable = lable; } }

BookService.java :

@Service public class BookService { @Autowired private BookDao bookDao3; }

配置类:

@Configuration @ComponentScan({"com.atguigu.service","com.atguigu.dao", "com.atguigu.controller","com.atguigu.bean"}) public class MainConifgOfAutowired { @Primary @Bean("bookDao2") public BookDao bookDao2(){ BookDao bookDao = new BookDao(); bookDao.setLable("2"); return bookDao; } }

测试类:

public class IOCTest_Autowired { @Test public void test01(){ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConifgOfAutowired.class); printBeans(applicationContext); BookService bookService = applicationContext.getBean(BookService.class); System.out.println(bookService); } }

说明 Spring 的bean容器中 有 bookDao,@Primary的 bookDao2 。 现在要装配 @Autowired 的 bookDao3,Spring 会将 bookDao2注入进去。

12.2、示例1 @Qualifier、@Primary

与示例1的区别是 BookService 中增加了 @Qualifier("bookDao")

BookService.java :

@Service public class BookService { @Qualifier("bookDao") @Autowired private BookDao bookDao3; }

说明 Spring 会将 bookDao 注入进去。

13、@Resource

Spring还支持使用 @Resource 和 @Inject —— java规范的注解。

@Resource 默认是按照组件名称进行装配的;也支持 指定组件名称 来进行装配 @Resource(name="beanDao2") 但是不支持 @Primary 功能, 没有支持 @Autowired(reqiured=false) 空装配。

14、 @Inject

需要导入javax.inject的包,和Autowired的功能一样。没有required=false的功能;

<dependency> <groupId>javax.inject</groupId> <artifactId>javax.inject</artifactId> <version>1</version> </dependency>
最新回复(0)