-
默认优先按照去容器中找对应的组件:applicationContext.getBean()
-
如果找到多个相同类型的组件,再将属性的名称作为组件的ID去容器中查找
-
@Qualifier()注解:该注解指定需要装配的组件ID,而不是使用属性名
-
自动装配默认必须要对属性赋值,没有就会报错,可以使用
@Autowired(required = false)
指定非必须就不会报错 -
@Primary注解:自动装配时当出现多个bean候选者时,被注解为
@Primary
的bean将作为首选者,否则将抛出异常,如果使用了@Qualifier()
指定装配的bean,则还是使用明确指定装配的bean
@Resource(JSR250)和@Inject(JSR330)(JDK提供的)
@Resource:
-
默认按照组件名称进行装配,也可以指定名称进行装配
-
当找不到与名称匹配的bean会按类型装配
-
不支持
@Primary
和@Autowired(required = false)
功能 -
如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。
-
如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。
-
如果指定了type,则从上下文中找到类似匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常。
-
如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配。
@Inject:
- 需要导入javax.inject的包,和Autowired功能一样,但是没有
required=false
功能
【3】@Autowired和@Resource注解的区别
-
@Autowired由Spring提供,只按照byType注入;@Resource由J2EE提供,默认按照byName自动注入,当找不到与名称匹配的bean会按类型装配
-
@Autowired默认按类型装配,默认情况下必须要求依赖对象存在,如果要允许null值,可以设置它的required属性为false。如果想使用名称装配可以结合@Qualifier注解进行使用。
-
@Resource,默认按照名称进行装配,名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行名称查找。如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
二、实例分析
这里只对@Autowired注解标注在属性位置进行实例分析
【1】@Autowired注解
// 启动类
@Test
public void TestMain() {
// 创建IOC容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = applicationContext.getBean(UserService.class);
System.out.println(“userService:” + userService);
}
// Service
@Service
public class UserService {
@Autowired(required = false) // 指定非必须
@Qualifier(“userDao2”) // 指定装配bean
private UserDao userDao;
@Override
public String toString() {
return “UserService{” +
“userDao=” + userDao +
‘}’;
}
}
// Dao
@Repository
public class UserDao {
private String label = “1”;
public void setLabel(String label) {
this.label = label;
}
@Override
public String toString() {
return “UserDao{” +
“label='” + label + ‘’’ +
‘}’;
}
}
// 配置类
@Configuration
@ComponentScan({“dao”,“service”,“controller”})
public class AppConfig {
@Primary // 首选装配bean
@Bean(“userDao2”)
public UserDao userDao(){
UserDao userDao = new UserDao();
userDao.setLabel(“2”);
return userDao;
}
}
输出结果如下,由于上面使用@Qualifier("userDao2")
指定了要装配的bean,所以这里输出的是label=’2‘:
- 如果将
@Qualifier("userDao2")
改为@Qualifier("userDao")
,则装配的是label=’1‘
【2】@Resource注解
@Service
public class UserService {
@Resource(name = “userDao2”,type = UserDao.class)
private UserDao userDao;
@Override
public String toString() {
return “UserService{” +
“userDao=” + userDao +
‘}’;
}
}
- 默认按照组件名称进行装配,也可以指定名称进行装配
- 当找不到与名称匹配的bean会按类型装配
- 不支持
@Primary
和@Autowired(required = false)
功能
- 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。
- 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。
- 如果指定了type,则从上下文中找到类似匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常。
- 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配。
三、源码追踪
这里对@Autowired注解底层进行源码分析
参考:https://blog.csdn.net/topdeveloperr/article/details/87971446
@Autowired是用来装配bean的,肯定和bean的实例化有关,先经过了refresh方法,在finishBeanFactoryInitialization方法中getBean,然后走getObject的时候触发bean的初始化。bean的初始化是一个很复杂地方,在AbstractAutowireCapableBeanFactory#doCreateBean方法中,先创建一个BeanWrapper,它的内部成员变量wrappedObject中存放的就是实例化的MyService对象,Spring Bean的生命周期源码详解 - 【Spring底层原理】,再往后进入populateBean方法进行属性注入
Spring对autowire注解的实现逻辑位于类:AutowiredAnnotationBeanPostProcessor#postProcessProperties
之中,——>findAutowiringMetadata——>buildAutowiringMetadata,核心代码就在buildAutowiringMetadata方法里面
private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) {
if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
return InjectionMetadata.EMPTY;
} else {
List elements = new ArrayList();
// 需要处理的目标类
Class targetClass = clazz;
do {
List currElements = new ArrayList();
// 通过反射获取该类所有的字段,并遍历每一个字段,并通过方法findAutowiredAnnotation遍历每一个字段的所用注解,并如果用autowired修饰了,则返回auotowired相关属性
ReflectionUtils.doWithLocalFields(targetClass, (field) -> {
MergedAnnotation<?> ann = this.findAutowiredAnnotation(field);
if (ann != null) {
// 校验autowired注解是否用在了static方法上
if (Modifier.isStatic(field.getModifiers())) {
if (this.logger.isInfoEnabled()) {
this.logger.info("Autowired annotation is not supported on static fields: " + field);
}
return;
}
// 判断是否指定了required
boolean required = this.determineRequiredStatus(ann);
currElements.add(new AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement(field, required));
}
});
// 和上面一样的逻辑,但是是通过反射处理类的method
ReflectionUtils.doWithLocalMethods(targetClass, (method) -> {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
MergedAnnotation<?> ann = this.findAutowiredAnnotation(bridgedMethod);
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
if (Modifier.isStatic(method.getModifiers())) {
if (this.logger.isInfoEnabled()) {
this.logger.info("Autowired annotation is not supported on static methods: " + method);
}
return;
}
if (method.getParameterCount() == 0 && this.logger.isInfoEnabled()) {
this.logger.info("Autowired annotation should only be used on methods with parameters: " + method);
}
boolean required = this.determineRequiredStatus(ann);
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new AutowiredAnnotationBeanPostProcessor.AutowiredMethodElement(method, required, pd));
}
}
});
// 用@Autowired修饰的注解可能不止一个,因此都加在currElements这个容器里面,一起处理
elements.addAll(0, currElements);
targetClass = targetClass.getSuperclass();
} while(targetClass != null && targetClass != Object.class);
return InjectionMetadata.forElements(elements, clazz);
}
}
- 获取需要处理的目标类
- 通过doWithLocalFields方法传入目标类参数,通过反射获取该类所有的字段,并遍历每一个字段,并通过方法findAutowiredAnnotation遍历每一个字段的所用注解,并如果用autowired修饰了,则返回auotowired相关属性
- 判断autowired注解是否用在了static方法上
- 如有多个@Autowired修饰的注解,都加在currElements这个容器里面,一起处理
最后返回包含所有带有autowire注解修饰的一个InjectionMetadata集合,如下
-
targetClass:要处理的目标类
-
elements:上述方法获取到的所以elements集合
public InjectionMetadata(Class<?> targetClass, Collection<InjectionMetadata.InjectedElement> elements) {
this.targetClass = targetClass;
this.injectedElements = elements;
}
有了目标类,与所有需要注入的元素集合之后,我们就可以实现autowired的依赖注入逻辑了,实现的方法如下:
public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) {
if (!this.validatedBeanNames.contains(beanName)) {
if (!this.shouldSkip(this.beanFactory, beanName)) {
List invalidProperties = new ArrayList();
PropertyDescriptor[] var6 = pds;
int var7 = pds.length;
for(int var8 = 0; var8 < var7; ++var8) {
PropertyDescriptor pd = var6[var8];
if (this.isRequiredProperty(pd) && !pvs.contains(pd.getName())) {
invalidProperties.add(pd.getName());
}
}
if (!invalidProperties.isEmpty()) {
throw new BeanInitializationException(this.buildExceptionMessage(invalidProperties, beanName));
}
}
this.validatedBeanNames.add(beanName);
}