spring如何扫描@Autowired、@Value、@Resource

前面文章@Autowired与@Resource区别主要对这两个注解的差异及使用方式进行了一个总结,本文将从源码分析被@Autowired、@Value和@Resource这三个注解修饰的属性和方法是如何被spring扫描到的。

其实负责扫描这三个注解的类是spring中的一种后置处理器BeanPostProcessor。其中,AutowiredAnnotationBeanPostProcessor负责扫描@Autowired和@Value这两个注解;CommonAnnotationBeanPostProcessor负责扫描@Resource这个注解。

平时开发中,我们通常将@Autowired、@Value和@Resource这三个注解添加到属性上,实际上这三个注解也可以应用在方法上,spring同样可以将它们扫描到。

下面将对核心源码进行简单分析:

@Autowired和@Value

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata()

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

    do {
        final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
        // 解析属性上的注解
        ReflectionUtils.doWithLocalFields(targetClass, field -> {
            // 判断属性上是否有注解@Autowired或@Value
            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;
                }
                // 推断该属性是否是必须的,@Value的required永远为true
                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;
            }
            // 查找方法上是否有@Autowired或@Value注解
            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;
                }
                // @Autowired和@Value应该应用于有参数的方法上
                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));
            }
        });

        elements.addAll(0, currElements);
        // 获取当前bean的父类,如果父类不是Object,继续循环,判断父类中是否添加了@Autowired、@Value属性
        targetClass = targetClass.getSuperclass();
    }
    while (targetClass != null && targetClass != Object.class);

    return new InjectionMetadata(clazz, elements);
}

上面核心代码的主要逻辑如下:

(1)@Autowired和@Value这两个注解加在属性上:加在属性字段上,该字段不能被static修饰,否则无法注入,显示默认值null;

(2)@Autowired和@Value这两个注解加在方法上:加在方法上,方法应该包含参数,并且该方法不能被static修饰,否则无法注入,显示默认值null。

(3)如果该类拥有父类,递归查找父类添加了这两个注解的属性和方法,执行同样的扫描逻辑。

@Autowired和@Value代码测试:

@Service
public class InstanceA implements InstanceAPI{
    // 正常注入
    @Autowired
    private InstanceB instanceB;

    // 不报错:为null
    @Autowired
    private static InstanceC instanceC;

    private InstanceD instanceD;
    // 正常注入
    @Autowired
    public void instanceD(InstanceD instanceD){
        this.instanceD = instanceD;
    }

    private InstanceE instanceE;
    // 不报错:instanceE为null
    @Autowired
    public static void instanceE(InstanceE instanceE){
        instanceE = instanceE;
    }

    // 正常注入
    @Value("1")
    private Integer numValueA;

    // 不报错:为null
    @Value("2")
    private static Integer numValueB;

    // 正常注入
    private Integer numValueC;
    @Value("3")
    public void setNumC(Integer numC){
        this.numValueC = numC;
    }

    // 不报错:为null
    private static Integer numValueD;
    @Value("4")
    public static void setNumD(Integer numD){
        numValueD = numD;
    }
}

spring如何扫描@Autowired、@Value、@Resource

@Autowired和@Value代码测试结果

@Resource

org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#buildResourceMetadata()

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

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

        ReflectionUtils.doWithLocalFields(targetClass, field -> {
            if (webServiceRefClass != null && field.isAnnotationPresent(webServiceRefClass)) {
                if (Modifier.isStatic(field.getModifiers())) {
                    throw new IllegalStateException("@WebServiceRef annotation is not supported on static fields");
                }
                currElements.add(new WebServiceRefElement(field, field, null));
            }
            else if (ejbRefClass != null && field.isAnnotationPresent(ejbRefClass)) {
                if (Modifier.isStatic(field.getModifiers())) {
                    throw new IllegalStateException("@EJB annotation is not supported on static fields");
                }
                currElements.add(new EjbRefElement(field, field, null));
            }
            // 判断属性上是否有注解@Resource
            else if (field.isAnnotationPresent(Resource.class)) {
                // 判断属性是否是静态
                if (Modifier.isStatic(field.getModifiers())) {
                    throw new IllegalStateException("@Resource annotation is not supported on static fields");
                }
                // 判断属性是否被忽略注入
                if (!this.ignoredResourceTypes.contains(field.getType().getName())) {
                    currElements.add(new ResourceElement(field, field, null));
                }
            }
        });

        ReflectionUtils.doWithLocalMethods(targetClass, method -> {
            Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
            if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
                return;
            }
            if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
                if (webServiceRefClass != null && bridgedMethod.isAnnotationPresent(webServiceRefClass)) {
                    if (Modifier.isStatic(method.getModifiers())) {
                        throw new IllegalStateException("@WebServiceRef annotation is not supported on static methods");
                    }
                    if (method.getParameterCount() != 1) {
                        throw new IllegalStateException("@WebServiceRef annotation requires a single-arg method: " + method);
                    }
                    PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
                    currElements.add(new WebServiceRefElement(method, bridgedMethod, pd));
                }
                else if (ejbRefClass != null && bridgedMethod.isAnnotationPresent(ejbRefClass)) {
                    if (Modifier.isStatic(method.getModifiers())) {
                        throw new IllegalStateException("@EJB annotation is not supported on static methods");
                    }
                    if (method.getParameterCount() != 1) {
                        throw new IllegalStateException("@EJB annotation requires a single-arg method: " + method);
                    }
                    PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
                    currElements.add(new EjbRefElement(method, bridgedMethod, pd));
                }
                // 判断方法上是否有注解@Resource
                else if (bridgedMethod.isAnnotationPresent(Resource.class)) {
                    // 被@Resouce修饰的方法不能是静态的
                    if (Modifier.isStatic(method.getModifiers())) {
                        throw new IllegalStateException("@Resource annotation is not supported on static methods");
                    }
                    Class<?>[] paramTypes = method.getParameterTypes();
                    // 被@Resouce修饰的方法只能有1个参数
                    if (paramTypes.length != 1) {
                        throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);
                    }
                    // 判断方法传递进来的bean是否被忽略注入
                    if (!this.ignoredResourceTypes.contains(paramTypes[0].getName())) {
                        PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
                        currElements.add(new ResourceElement(method, bridgedMethod, pd));
                    }
                }
            }
        });

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

    return new InjectionMetadata(clazz, elements);
}

上面核心代码的主要逻辑如下:

(1)@Resource注解加在属性上:注解被加在属性字段上时,该属性字段不能被static修饰,否则直接报错;

(2)@Resource注解加在方法上:注解被加在方法上时,该方法必须只有1个参数,并且该方法不能被static修饰,否则直接报错。

(3)如果该类拥有父类,递归查找父类添加了这个注解的属性和方法,执行同样的扫描逻辑。

@Resource代码测试:

@Service
public class InstanceA implements InstanceAPI{
    // 正常注入
    @Resource
    private InstanceB instanceB;

    // 报错:@Resource不能修饰静态属性
    @Resource
    private static InstanceC instanceC;

    private InstanceD instanceD;
    // 正常注入
    @Resource
    public void instanceD(InstanceD instanceD){
        this.instanceD = instanceD;
    }

    private InstanceE instanceE;
    // 报错:@Resource不能修饰静态方法
    @Resource
    public static void instanceE(InstanceE instanceE){
        instanceE = instanceE;
    }
}

 

上一篇:IOC自动注入


下一篇:Bean自动装配原理