Spring框架中@Autowired是如何工作的

本文详细介绍了Spring框架中的@Autowired注解,用于自动装配Bean。内容包括@Autowired的作用、用法,以及如何通过源码分析了解其工作原理。通过字段和方法的扫描,实现依赖注入,简化了Bean的配置。同时,文章还提到了与@Qualifier和@Resource等注解的区别和配合使用。在找不到匹配Bean时,会抛出异常。最后,文章总结了@Autowired的核心流程。
摘要由CSDN通过智能技术生成

目录

@Autowired注解用法

@Autowired注解是干什么用的

自动装配

源码分析

findAutowiringMetadata()

 inject()

 注意:

总结


讲解之前我们先来看下视频!!! 

​​​​​​​​​​​​​​趣味Spring-2分钟动画讲解@Autowired是如何工作的

在Spring框架中 @Autowired是一种注解,可以对成员变量、方法和构造函数进行标注,来完成自动装配的工作,@Autowired标注可以放在成员变量上,也可以放在成员变量的set方法上,也可以放在任意方法上表示,自动执行当前方法,如果方法有参数,会在IOC容器中自动寻找同类型参数为其传值。

Spring 2.5 引入了 @Autowired 注释,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。 通过 @Autowired的使用来消除 set ,get方法。

@Autowired注解用法

 将@Autowired注释应用于方法

  将@Autowired注释应用于属性上

@Autowired注解是干什么用的

@Autowired从所属范围来看,事实上属于spring的容器配置的一个注解,与它同属容器配置的注解还有: 

             @Primary
                  含义:在类型冲突情况下此注解修饰的类作为首选项,
                  位置:修饰类
                  注意:不能单独使用
             @Qualifier(value="名称")
                  含义:按照名称装配
                  位置:修饰成员变量
                  注意:不能单独使用,必须与@Autowire联合使用
             @Resource(name="名称")
                  含义:按照名称装配,指定一个ID名称固定使用
                  位置:修饰成员变量
                  注意:单独使用
               @Scope
                  含义:作用域
                  位置:修饰类
                  语法:@Scope("singleton")
                       @Scope("prototype")
                        @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
                        @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

自动装配

然而从字面上来看@autowired注解来源于英文单词autowire,这个单词的意思是自动装配的意思。自动装配又是什么意思?这个词语本来的意思是指的一些工业上的用机器代替人口,自动将一些需要完成的组装任务,或者别的一些任务完成。而在spring的世界当中,自动装配指的就是使用将Spring容器中的bean自动的和我们需要这个bean的类组装在一起,并且Spring利用依赖注入(DI)完成对IOC容器中各个组件的依赖关系赋值

@Autowired自动注入:

(1)默认优先按照类型去容器中找对应的组件:applicationContext.getBean()
(2)如果找到多个相同类型的组件,再将属性的名称作为组件的ID去容器中查找


源码分析

@Autowired 注解是由 AutowiredAnnocationBeanPostProcessor 来处理的。Spring会在实例化Bean时有两个触发点:

在方法 postProcessMergedBeanDefinition 中,查找该Bean是否有 @Autowired
注解

在方法 populateBean 进行属性注入时,也会查找该Bean是否有 @Autowired 注解。

public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor,
		MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware {

     // 省略其他属性和方法
            
     // postProcessMergedBeanDefinition方法会调用这个入口点
     @Override
	public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
		InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
		metadata.checkConfigMembers(beanDefinition);
	}
   
            
    // populateBean方法会调用这个入口点        
    @Override
	public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
        // ....
        InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
        // 对bean对象进行属性注入
	   metadata.inject(bean, beanName, pvs);
    }
}

findAutowiringMetadata()


findAutowiringMetadata()方法是用来查找类的字段、构造函数、方法是否被 @Autowired 标记;如果有,就把字段、构造函数、方法封装成一个注入点 InjectionElement

findAutowiringMetadata()方法先从注入点缓存中查找,是否有被缓存过的,有就直接返回;没有就调用 buildAutowiringMetadata 方法对类的字段、方法、构造函数进行解析、查找 @Autowired,并向父类上继续查找。这里直接来看 buildAutowiringMetadata 方法。
 

private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
		// 判断指定类是否是 @Autowire @Value的候选类
		if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
			return InjectionMetadata.EMPTY;
		}

		List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
		Class<?> targetClass = clazz;

		do {
			final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
			// 在类的字段上找@Autowired注解
			ReflectionUtils.doWithLocalFields(targetClass, field -> {
				MergedAnnotation<?> ann = findAutowiredAnnotation(field);
				if (ann != null) {
					if (Modifier.isStatic(field.getModifiers())) {
						if (logger.isInfoEnabled()) {
							logger.info("Autowired annotation is not supported on static fields: " + field);
						}
						return;
					}
					boolean required = determineRequiredStatus(ann);
					currElements.add(new AutowiredFieldElement(field, required));
				}
			});
			
            // 在类的构造函数和方法上找@Autowired注解
            // 主要:方法不能是静态方法
			ReflectionUtils.doWithLocalMethods(targetClass, method -> {
				Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
				if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
					return;
				}
				MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
				if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
					if (Modifier.isStatic(method.getModifiers())) {
						if (logger.isInfoEnabled()) {
							logger.info("Autowired annotation is not supported on static methods: " + method);
						}
						return;
					}
					if (method.getParameterCount() == 0) {
						if (logger.isInfoEnabled()) {
							logger.info("Autowired annotation should only be used on methods with parameters: " +
									method);
						}
					}
					boolean required = determineRequiredStatus(ann);
					PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
					currElements.add(new AutowiredMethodElement(method, required, pd));
				}
			});

			elements.addAll(0, currElements);
			targetClass = targetClass.getSuperclass();
		}
		while (targetClass != null && targetClass != Object.class);

		return InjectionMetadata.forElements(elements, clazz);
	}

 inject()


inject 方法就是属性的入口。AutowiredMethodElementAutowiredFieldElement 分别代表字段注入、方法注入点。接下来看一下 AutowiredFieldElement 的 inject 方法。

这个方法大概逻辑:如果该注入点已经被注入过(this.cached=true),就直接从缓存中获取;否则就有解析这个字段,从BeanFactory容器中找到合适的候选Bean,最后通过 field.set(bean, value) 注入。

protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    Field field = (Field) this.member;
    Object value;
    if (this.cached) {
        try {
            value = resolvedCachedArgument(beanName, this.cachedFieldValue);
        }
        catch (NoSuchBeanDefinitionException ex) {
            // Unexpected removal of target bean for cached argument -> re-resolve
            value = resolveFieldValue(field, bean, beanName);
        }
    }
    else {
        value = resolveFieldValue(field, bean, beanName);
    }
    if (value != null) {
        ReflectionUtils.makeAccessible(field);
        field.set(bean, value);
    }
}

 从源码中我们看到,核心逻辑就是通过beanFactory.resolveDependency()方法从bean工厂中获取依赖的对象。跟踪一下resolveDependency()方法可以发现,底层会调用到org.springframework.beans.factory.config.DependencyDescriptor#resolveCandidate()方法,而resolveCandidate()方法内部其实是通过beanFactory.getBean("类对象")从bean容器中获取到这个bean对象。

 注意:


     1.@Autowire默认按照类型装配
      2.如果spring容器中没有可以与之匹配的类,则会报异常
               NoSuchBeanDefinitionException
       3.如果spring容器中有多个类型可以与之匹配,@Autowire注解会自动切换为按照名称装配,如果还没有则会报错
                NoUniqueBeanDefinitionException

总结

  • 根据Class目标类类型,通过反射获取其所有的Field和Method信息,然后判断字段或者方法上面是否添加了@Autowired@Value注解,以此来判断是否需要自动装配;
  • 如果标注有@Autowired@Value注解,表示需要自动装配,则会将需要自动装配的元素,封装成AutowiredFieldElement(针对字段)AutowiredMethodElement(针对方法)对象;
  • 调用AutowiredFieldElementAutowiredMethodElement的inject方法,通过反射,调用容器的getBean()方法找到需要注入的Bean对象,然后注入到目标Bean中;
     
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值