@Autowired 注解的工作原理

一、概述

1、注解用法

根据@Autowired注解的源码,可以看到该注解可以作用在构造器、参数、方法、属性,都是从容器中获取参数组件的值

标注在方法上:@Bean+方法参数,参数从容器中获取,默认不写@Autowired效果是一样的,都能自动装配
标注在构造器上:如果组件上只有一个有参构造,这个有参构造的@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;

}

2、自动装配

Spring利用依赖注入(DI)完成对IOC容器中各个组件的依赖关系赋值

@Autowired自动注入:

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

二、源码分析

这里对@Autowired注解底层进行源码分析

@Autowired是用来装配bean的,肯定和bean的实例化有关,先经过了refresh方法,在finishBeanFactoryInitialization方法中getBean,然后走getObject的时候触发bean的初始化。bean的初始化是一个很复杂地方,在AbstractAutowireCapableBeanFactory#doCreateBean方法中,先创建一个BeanWrapper,它的内部成员变量wrappedObject中存放的就是实例化的MyService对象,再往后进入populateBean方法进行属性注入

Spring对autowire注解的实现逻辑位于类:

AutowiredAnnotationBeanPostProcessor

之中,到findAutowiringMetadata再到buildAutowiringMetadata,核心代码就在buildAutowiringMetadata方法里面

private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
		LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<>();
		// 需要处理的目标类
		Class<?> targetClass = clazz;

		do {
			final LinkedList<InjectionMetadata.InjectedElement> currElements = new LinkedList<>();

			// 通过反射获取该类所有的字段,并遍历每一个字段,并通过方法findAutowiredAnnotation遍历每一个字段的所用注解,并如果用autowired修饰了,则返回auotowired相关属性
			ReflectionUtils.doWithLocalFields(targetClass, field -> {
				AnnotationAttributes ann = findAutowiredAnnotation(field);
				if (ann != null) {
					if (Modifier.isStatic(field.getModifiers())) {
						if (logger.isWarnEnabled()) {
							logger.warn("Autowired annotation is not supported on static fields: " + field);
						}
						return;
					}
					boolean required = determineRequiredStatus(ann);
					currElements.add(new AutowiredFieldElement(field, required));
				}
			});

			ReflectionUtils.doWithLocalMethods(targetClass, method -> {
				Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
				if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
					return;
				}
				AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);
				if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
					if (Modifier.isStatic(method.getModifiers())) {
						if (logger.isWarnEnabled()) {
							logger.warn("Autowired annotation is not supported on static methods: " + method);
						}
						return;
					}
					if (method.getParameterCount() == 0) {
						if (logger.isWarnEnabled()) {
							logger.warn("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));
				}
			});
			//有多个@Autowired修饰的注解,都加在currElements这个容器里面,一起处理
			elements.addAll(0, currElements);
			targetClass = targetClass.getSuperclass();
		}
		while (targetClass != null && targetClass != Object.class);

		return new InjectionMetadata(clazz, elements);
	}

(1)获取需要处理的目标类
(2)通过doWithLocalFields方法传入目标类参数,通过反射获取该类所有的字段,并遍历每一个字段,并通过方法findAutowiredAnnotation遍历每一个字段的所用注解,并如果用autowired修饰了,则返回auotowired相关属性
(3)判断autowired注解是否用在了static方法上
(4)如有多个@Autowired修饰的注解,都加在currElements这个容器里面,一起处理

最后返回包含所有带有autowire注解修饰的一个InjectionMetadata集合

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<String> 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);
    }
 
    return pvs;
}

调用InjectionMetadata中定义的inject方法:

public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    Collection<InjectionMetadata.InjectedElement> checkedElements = this.checkedElements;
    Collection<InjectionMetadata.InjectedElement> elementsToIterate = checkedElements != null ? checkedElements : this.injectedElements;
    if (!((Collection)elementsToIterate).isEmpty()) {
        Iterator var6 = ((Collection)elementsToIterate).iterator();
 
        while(var6.hasNext()) {
            InjectionMetadata.InjectedElement element = (InjectionMetadata.InjectedElement)var6.next();
            element.inject(target, beanName, pvs);
        }
    }
}

进行遍历,然后调用inject方法,inject方法其实现逻辑如下:

protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs) throws Throwable {
    if (this.isField) {
        Field field = (Field)this.member;
        // 通过反射对对象进行实例化和赋值
        ReflectionUtils.makeAccessible(field);
        field.set(target, this.getResourceToInject(target, requestingBeanName));
    } else {
        if (this.checkPropertySkipping(pvs)) {
            return;
        }
 
        try {
            Method method = (Method)this.member;
            ReflectionUtils.makeAccessible(method);
            method.invoke(target, this.getResourceToInject(target, requestingBeanName));
        } catch (InvocationTargetException var5) {
            throw var5.getTargetException();
        }
    }
}

(1)使用了反射技术,分成字段和方法去处理。
(2)makeAccessible这样的可以称之为暴力破解的方法,通过反射技术对对象进行实例化和赋值
(3)getResourceToInject方法的参数就是要注入的bean的名字,这个方法就是根据这个bean的名字去拿到它

  • 12
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值