@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>@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默认就是方法名。
@ComponentScan 等价于 <context:component-scan base-package="xxx.xxx" ></context:component-scan>
value : 扫描的包路径。value 是数组类型的,可以指定多个路径。
excludeFilters = Filter[] :排除组件
includeFilters = Filter[] :只包含哪些组件
useDefaultFilters 运行默认过虑规则(默认是true,扫描所有的),如果 使用 includeFilters 时,一定要将 useDefaultFilters 设置为false。
FilterType.ANNOTATION:按照注解FilterType.ASSIGNABLE_TYPE:按照给定的类型;FilterType.ASPECTJ:使用ASPECTJ表达式FilterType.REGEX:使用正则指定FilterType.CUSTOM:使用自定义规则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}) })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); } }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; } }@Scope 对bean组件的作用域修饰,所以 @Bean 用在同一地方。
上面的代码等价于
<bean id="person" class="com.atguigu.bean.Person" scope="prototype" > <property name="name" value="张三"></property> <property name="age" value="25"></property> </bean>@Lazy 只针对 单实例bean。
单实例bean 默认在容器启动的时候创建对象。 使用懒加载后,容器启动不创建对象,第一次使用(获取)Bean创建对象,并初始化;
@Configuration public class MainConfig2 { @Lazy @Bean("person") public Person person(){ System.out.println("给容器中添加Person...."); return new Person("张三", 25); } }按照一定的条件进行判断,满足条件给容器中注册bean 。
|作用域|说明|备注| |–|--| |方法上| 表示满足条件,将此bean 注册到容器中 |一般要配合@Bean使用| |类上| 表示满足条件,这个类中配置的所有bean 才有可能 注册到容器 |一般要配合 @Configuration 使用|
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;
**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 ,显示不满足,所以无法被注册到容器中 。
@Import 使用说明: 参数是数组类型的,可以导入多个组件; id 默认是 组件的全类名 。
@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定义多个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 { }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); } }测试(略)
**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); } }bean的生命周期: bean创建 --> 初始化 —> 销毁的过程
容器管理bean的生命周期: 我们可以自定义初始化和销毁方法;容器在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")也不会打印。
执行测试,打印结果是
容器创建完成...解读:
如果是多实例,
Spring容器加载时,不会初始化这个bean;在使用这个bean时 (比如,执行 applicationContext.getBean("pig");),才会创建bean;Spring 容器关闭时,也不会调用销毁方法;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()可以使用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()在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() 。
@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>只能用于被spring 扫描的组件上面
@PropertySource(value = { "classpath:/person.properties" }) @Configuration @ConfigurationProperties(prefix="person") public class MainConfigOfPropertyValues { @Bean public Person person() { return new Person(); } }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=小李四]@Autowired 即 @Autowired(required=true),自动注入失败,则抛异常。
@Autowired(required=true) 自动注入规则:
首先在容器中查询对应类型的bean,
如果查询的结果 为空 ,那么会抛出异常。解决方法,使用required=false ,不会抛出异常。如果查询结果刚好为 一个 ,就将该bean装配给 @Autowired 指定的数据。如果查询的结果 不止一个 ,那么 @Autowired 会根据 属性名 来查找;如果无法匹配,则抛异常。解决方案:设置 @Autowired(required=false)、增加@Qualifier("bookDao3") 。@Autowired 和 @Qualifier() 配合指定 beanName 的b ean 进行装配。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),即 自动注入不是必须的 。
@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 。
@Primary:让 Spring 进行自动装配(@Autowired)的时候,默认使用首选的 bean; 也可以继续使用 @Qualifier 指定需要装配的 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注入进去。
与示例1的区别是 BookService 中增加了 @Qualifier("bookDao")
BookService.java :
@Service public class BookService { @Qualifier("bookDao") @Autowired private BookDao bookDao3; }说明 Spring 会将 bookDao 注入进去。
Spring还支持使用 @Resource 和 @Inject —— java规范的注解。
@Resource 默认是按照组件名称进行装配的;也支持 指定组件名称 来进行装配 @Resource(name="beanDao2") 但是不支持 @Primary 功能, 没有支持 @Autowired(reqiured=false) 空装配。
需要导入javax.inject的包,和Autowired的功能一样。没有required=false的功能;
<dependency> <groupId>javax.inject</groupId> <artifactId>javax.inject</artifactId> <version>1</version> </dependency>