美文网首页
spring依赖注入——注解注入

spring依赖注入——注解注入

作者: 编程易行 | 来源:发表于2018-09-02 14:10 被阅读36次

    之前分析spring的依赖注入时,主要分析的是xml配置方式。但是在实际项目中,我们其实用的更多的是注解方式。这一篇博客会分析下spring是如何处理这种注解注入的。(主要分析最常使用的@Autowired 和 @Resource注解)

    注解注入的开启 annotation-config

    SpringBoot方式暂且不管,正常来说我们要想启用注解注入都需要有这样一个配置:

    <beans xmlns="http://www.springframework.org/schema/beans"  
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
        xmlns:context="http://www.springframework.org/schema/context"  
        xsi:schemaLocation=" http://www.springframework.org/schema/beans  
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
           http://www.springframework.org/schema/context  
           http://www.springframework.org/schema/context/spring-context-3.0.xsd">  
      
        <context:annotation-config/>  
      
    </beans> 
    

    那么我们就从annotation-config的解析开始


    从代码很容易看出,annotation-config的解析是在org.springframework.context.annotation.AnnotationConfigBeanDefinitionParser类里。

    PostProcessor的注册

    对于annotation-config的处理,依然只关心最核心的部分。跟着源码走,发现一部分非常核心的逻辑是在AnnotationConfigUtils里。在处理annotation-config时候,spring配置了各种PostProcessor类

    ConfigurationClassPostProcessor
    AutowiredAnnotationBeanPostProcessor
    RequiredAnnotationBeanPostProcessor
    CommonAnnotationBeanPostProcessor
    ....

    populateBean

    我们在分析populateBean时候,只是曾跳过一段关于关于PostProcessor的回调方法。而Spring正是通过PostProcessor来实现注解方式的依赖注入的。

    同样,这里把PostProcessor这一段单独贴出来

    protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
        PropertyValues pvs = mbd.getPropertyValues();
    
        if (bw == null) {
            if (!pvs.isEmpty()) {
                throw new BeanCreationException(
                        mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
            }
            else {
                // Skip property population phase for null instance.
                return;
            }
        }
    
        // Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
        // state of the bean before properties are set. This can be used, for example,
        // to support styles of field injection.
        boolean continueWithPropertyPopulation = true;
    
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof InstantiationAwareBeanPostProcessor) {
                    InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                    if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
                        continueWithPropertyPopulation = false;
                        break;
                    }
                }
            }
        }
    
        if (!continueWithPropertyPopulation) {
            return;
        }
    
        //... 省略AutowiredMode等
    
        boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
        boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);
    
        //就是在这里处理注解形式的依赖注入的
        if (hasInstAwareBpps || needsDepCheck) {
            PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
            if (hasInstAwareBpps) {
                for (BeanPostProcessor bp : getBeanPostProcessors()) {
                    if (bp instanceof InstantiationAwareBeanPostProcessor) {
                        InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                        pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                        if (pvs == null) {
                            return;
                        }
                    }
                }
            }
            if (needsDepCheck) {
                checkDependencies(beanName, mbd, filteredPds, pvs);
            }
        }
    
        applyPropertyValues(beanName, mbd, bw, pvs);
    }
    

    @Autowired处理 AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues

    从annotatation-config的处理我们知道了,spring其实配置了很多的PostProcessor。这里我只分析我们最常用的一个AutowiredAnnotationBeanPostProcessor 这个类对@Autowired注解进行了处理

    AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues

    随后我们就来看看,究竟这个AutowiredAnnotationBeanPostProcessor是如何对这些依赖注入使用的注解进行处理的

    public PropertyValues postProcessPropertyValues(
            PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
    
        InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
        try {
            metadata.inject(bean, beanName, pvs);
        }
        catch (BeanCreationException ex) {
            throw ex;
        }
        catch (Throwable ex) {
            throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
        }
        return pvs;
    }
    

    这里的代码很简单,找到InjectionMetadata
    调用InjectionMetadata进行注入

    那么我们就要先弄清楚,这个InjectionMetadata究竟是什么?

    Internal class for managing injection metadata.
    Not intended for direct use in applications.

    注释里说是一个用于管理注入的元数据的管理类,到这里我们还是不清楚它是干啥用的。

    所以继续深入分析InjectionMetadata是如何构建的。findAutowiringMetadata这个方法大概就是,如果有的话,就从缓存获取,如果没有的话,就build一个。所以我们直接看build的逻辑就好了

    private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
        LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<>();
        Class<?> targetClass = clazz;
    
        do {
            final LinkedList<InjectionMetadata.InjectedElement> currElements =
                    new LinkedList<>();
    
            //找到所有的标注了 @Autowired @Value 等注解的field,封装成一个AutowiredFieldElement 添加到currElements里
            ReflectionUtils.doWithLocalFields(targetClass, new ReflectionUtils.FieldCallback() {
                @Override
                public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
                    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));
                    }
                }
            });
    
            //找到所有的标注了 @Autowired @Value 等注解的method,封装成一个AutowiredFieldElement 添加到currElements里
            ReflectionUtils.doWithLocalMethods(targetClass, new ReflectionUtils.MethodCallback() {
                @Override
                public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
                    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 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);
        
        //封装找到了的method和field,放到InjectionMetadata
        return new InjectionMetadata(clazz, elements);
    }
    

    这里的逻辑也很简单
    1)找到所要注入的类,标记有 @Autowired @Value等注解的 field和method 添加到一个list中
    2)以list和这个class封装成一个InjectionMetadata
    3)注意这里的while (targetClass != null && targetClass != Object.class); 除了扫描自己类的@Autowired等注解,还会扫描父类的注解,这也解释了,为什么继承了抽象类的时候,可以同时继承其 @Autowired等注解

    下面的例子简单说明了父类注入的场景

    抽象类的注入

    @Component
    public class BaseDao {
    
        //各种数据库操作方式...
    }
    
    //抽象类,只是注入了dao
    public abstract class BaseService {
    
        @Autowired
        protected BaseDao dao;
    
        public BaseDao getDao() {
            return dao;
        }
    
        public void setDao(BaseDao dao) {
            this.dao = dao;
        }
    }
    

    StudentService 继承了BaseService,这样就可以直接在自己的方法里使用dao

    public class StudentService extends BaseService {
    }
    

    xml配置,xml只需要配置Service和dao,就会自动将dao注入到StudentService中

    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    
        <!--<context:component-scan base-package="com.hdj.learn.spring.annotation"/>-->
    
        <bean id="dao" class="com.hdj.learn.spring.annotation.abs.BaseDao">
        </bean>
    
        <bean id="studentService" class="com.hdj.learn.spring.annotation.abs.StudentService"/>
    
        <context:annotation-config/>
    </beans>
    

    @Autowired的继承正是通过上文分析的,一个简单的do while就实现了。

    回到 InjectionMetadata

    经过上文的分析,我们就大概知道了所谓的InjectionMetadata究竟是个什么。它存储了某个类,以及这个类里需要被依赖注入的element(标注了@Autowired,@Value等注解的方法或者成员变量)

    分析到这里后,随后的就是InjectionMetadata.inject方法。
    大概就是遍历之前的List<InjectedElement> 然后调用 InjectedElement.inject方法

    protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
        Field field = (Field) this.member;
        Object value;
        if (this.cached) {
            value = resolvedCachedArgument(beanName, this.cachedFieldValue);
        }
        else {
            DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
            desc.setContainingClass(bean.getClass());
            Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
            TypeConverter typeConverter = beanFactory.getTypeConverter();
            try {
                //找到要注入的值
                value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
            }
            catch (BeansException ex) {
                throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
            }
            synchronized (this) {
                if (!this.cached) {
                    if (value != null || this.required) {
                        this.cachedFieldValue = desc;
                        registerDependentBeans(beanName, autowiredBeanNames);
                        if (autowiredBeanNames.size() == 1) {
                            String autowiredBeanName = autowiredBeanNames.iterator().next();
                            if (beanFactory.containsBean(autowiredBeanName)) {
                                if (beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
                                    this.cachedFieldValue = new ShortcutDependencyDescriptor(
                                            desc, autowiredBeanName, field.getType());
                                }
                            }
                        }
                    }
                    else {
                        this.cachedFieldValue = null;
                    }
                    this.cached = true;
                }
            }
        }
        if (value != null) {
            ReflectionUtils.makeAccessible(field);
            field.set(bean, value);
        }
    }
    

    特别熟悉的代码,大体和之前通过xml依赖注入的代码一样。找到要注入的对象,注入进去。

    总结

    这篇博客分析了spring对于注解标记的变量,是如何进行注入了。主要分析了我们最常用的 @Autowired 、@Value 注解。依赖注入分析到这里,基本上就能大致理解spring依赖注入的步骤。当然还是有种无法统一的感觉,下一篇博客会试着从上层到下分析spring依赖注入究竟是如何设计的,以及从spring的依赖注入中我们能学到什么。

    相关文章

      网友评论

          本文标题:spring依赖注入——注解注入

          本文链接:https://www.haomeiwen.com/subject/ustmwftx.html