所謂spring自動注入,是指容器中的一個組件中需要用到另一個組件(例如聚合關系)時,依靠spring容器創建對象,而不是手動創建,主要有三種方式:
1. @Autowired注解——由spring提供
2. @Resource注解——由JSR-250提供
3. @Inject注解——由JSR-330提供
@Autowired注解的使用方法
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Autowired { /** * Declares whether the annotated dependency is required. * <p>Defaults to {@code true}. */ boolean required() default true; }
從源碼可以看出:該注解可以用在構造器、方法、參數、屬性上,最常見的是用在屬性上。
該注解只有一個屬性: required,默認為true,如果找不到匹配的bean則報錯;設置為false,如果找不到匹配的bean則注入null,並不會報錯。
可以配合@Qualifier使用,用於精准指定要注入的bean的名稱。
可以配合@Primary使用,當容器中存在多個相同類型的組件時,用於指定優先加載哪一個,這個注解不能用在2個或更多同類型的組件上。
1. 當容器中只有一個該類型的組件時
|-不使用@Qualifier,會按bean類型查找,即applicationContext.getBean(bean.class),然后注入這個唯一的bean。
|-使用@Qualifier,會按Qualifier的value值跟bean名稱匹配查找,即applicationContext.getBean("Qualifier的value值")。
2. 當容器中沒有該類型的組件時
|-required=true——報錯expected at least 1 bean which qualifies as autowire candidate。
|-required=false——注入null。
3. 當容器中存在多個該類型的組件時
|-不使用@Qualifier和@Primary時,會按屬性名跟bean名稱匹配查找,即applicationContext.getBean("屬性名")。
|-使用@Primary、但不使用@Qualifier時,會優先加載帶有@Primary注解的組件。
|-使用@Qualifier時,不管有沒有使用@Primary,都會直接按Qualifier的value值跟bean名稱匹配查找。
綜上,當容器中存在多個同類型的組件時,加載優先級:@Qulifier>@Primary>屬性名,例如下面這個容器中包含3個BookDao組件
@Configuration @ComponentScan(basePackages = {"cn.monolog.service"}) public class AutowiredBeanConfig { @Bean(value = "bookDao1") public BookDao bookDao1() { BookDao bookDao = new BookDao(); bookDao.setLabel("bookDao1"); return bookDao; } @Bean(value = "bookDao2") @Primary public BookDao bookDao2() { BookDao bookDao = new BookDao(); bookDao.setLabel("bookDao2"); return bookDao; } @Bean(value = "bookDao3") public BookDao bookDao3() { BookDao bookDao = new BookDao(); bookDao.setLabel("bookDao3"); return bookDao; } }
自動注入方式是這樣的:
@Autowired @Qualifier(value = "bookDao1") private BookDao bookDao3;
按照優先級順序,@Qulifier(bookDao1) > @Primary(bookDao2) > 屬性名(bookDao3),最終加載的是名稱為bookDao1的組件。
@Resource注解的使用方法
@Resource注解的使用跟@Autowired注解類似,但是需要注意:
1. 不支持@Primary注解,也不支持reuqired=false,即不允許注入null;
2. 該注解有一個屬性name,類似於@Qualified精准匹配,優先級最高;
3. 默認按照屬性名跟bean的名稱匹配查找,如果不存在,再按類型匹配查找。
例如,下面這個容器中有兩個CarDao組件
@Configuration @ComponentScan(basePackages = {"cn.monolog.service"}) public class ResourceBeanConfig { @Bean(value = "carDao1") public CarDao carDao1() { CarDao carDao = new CarDao(); carDao.setLabel("1"); return carDao; } @Bean(value = "carDao2") public CarDao carDao() { CarDao carDao = new CarDao(); carDao.setLabel("2"); return carDao; } }
自動注入方式如下,會按屬性名注入carDao2組件。
//自動注入 @Resource private CarDao carDao2;
但是改為下面這種注入方式:
//自動注入 @Resource(name = "carDao1") private CarDao carDao2;
由於使用了name精准匹配,會忽略屬性名,注入carDao1組件。
@Inject注解的使用方法
@Inject注解的使用方法跟@Autowired也基本相似,但是需要注意
1. 使用前需要導入jar包——javax.inject;
2. 支持@Primary注解,而且因為沒有精確匹配,@Primary的優先級最高;
2. 不支持required=false,即不能注入null,如果找不到組件肯定報錯;
3. 默認按照屬性名跟bean的名稱匹配查找,如果不存在,再按類型匹配查找。
例如,下面這容器中有一個EmployeeDao組件
@Configuration public class InjectBeanConfig { @Bean(value = "employeeDao1") public EmployeeDao employeeDao1() { EmployeeDao employeeDao = new EmployeeDao(); employeeDao.setLabel("1"); return employeeDao; } }
自動注入的方式如下
@Inject private EmployeeDao employeeDao3;
spring會先按屬性名查找名稱為employeDao3的組件,即applicationContext.getBean("employeeDao3"),結果不存在;
然后按照類型查找,即applicationContext.getBean(EmployeDao.class),找到employeDao1組件,成功注入。
如果容器中有多個同類型組件,例如
@Configuration public class InjectBeanConfig { @Bean(value = "employeeDao1") public EmployeeDao employeeDao1() { EmployeeDao employeeDao = new EmployeeDao(); employeeDao.setLabel("1"); return employeeDao; } @Bean(value = "employeeDao2") public EmployeeDao employeeDao2() { EmployeeDao employeeDao = new EmployeeDao(); employeeDao.setLabel("2"); return employeeDao; } @Bean(value = "employeeDao3") public EmployeeDao employeeDao3() { EmployeeDao employeeDao = new EmployeeDao(); employeeDao.setLabel("3"); return employeeDao; } }
注入方式還是這樣
@Inject private EmployeeDao employeeDao3;
仍然會按屬性名和bean的名稱匹配,即applicationContext.getBean("employeeDao3"),找到employee3,成功注入。
但是如果其中某個組件加了@Primary注解,會忽略屬性名,優先注入,例如
@Configuration public class InjectBeanConfig {
@Bean(value = "employeeDao1") public EmployeeDao employeeDao1() { EmployeeDao employeeDao = new EmployeeDao(); employeeDao.setLabel("1"); return employeeDao; }
@Bean(value = "employeeDao2") @Primary
public EmployeeDao employeeDao2() { EmployeeDao employeeDao = new EmployeeDao(); employeeDao.setLabel("2"); return employeeDao; }
@Bean(value = "employeeDao3") public EmployeeDao employeeDao3() { EmployeeDao employeeDao = new EmployeeDao(); employeeDao.setLabel("3"); return employeeDao; } }
無論注入時使用什么樣的屬性名,都會注入employeeDao2。