• Spring-Framework 源码阅读之@Autowired和AutowiredAnnotationBeanPostProcessor


      今天接下去讲我们的内容,上次的解析了AnnotationBeanUtils这个类的运用和源码。今天主要关注的是Autowired和 AutowiredAnnotationBeanPostProcessor这2个类。首先我们来看一下Autowired标签的定义。

    @Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Autowired {
    
        /**
         * 
         * <p>Defaults to {@code true}.
         */
        boolean required() default true;
    }

      从标签的定义和上面的注释可以知道,该标签可以用于构造函数、方法、参数、标签上。为了使这个标签生效,我们需要一个解析这个标签的类,Spring 为我们提供解析的类就是AutowiredAnnotationBeanPostProcessor,这是个BeanPostProcessor类。关于BeanPostProcessor大家可以查阅相关资料,之后等我读到这个接口的时候,会具体的和大家一起探讨。这里我们以参数注入为例,来分析了一下,这个类到底做了哪些事情。这个参数注入主要是它的一个内部类AutowiredFieldElement来处理。

      而这个类的inject()方法被调用是在AutowiredAnnotationBeanPostProcessor的postProcessPropertyValues方法上。

    @Override
    public PropertyValues postProcessPropertyValues(
            PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
    
        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;
    }

      从这里我们知道 findAutowiringMetadata()方法这里获取注入元数据信息,然后调用InjectionMetadata.inject()的方法。在以参数注入就是调用AutowiredFieldElement.inject()方法。

    这些类型的关系,将在之后解释。让我们具体来看一下 AutowiredFieldElement.inject()方法。

    @Override
    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<String>(1);
            TypeConverter typeConverter = beanFactory.getTypeConverter();
            try {
                //通过BeanFactory的resolveDependency()方法解决依赖的值。也就是这个参数需要注入的值
                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);
        }
    }

      接下来,我们需要知道,AutowiredAnnotationBeanPostProcessor的postProcessPropertyValues()方法什么时候被调用。在这个函数上,我们关注2个参数,一个是PropertyDescriptor

    类型的数组,一个beanName。beanName就是我们实例化的对象,其中PropertyDescriptor就是描述这个名为beanName的Bean的参数内容。在AbstractAutowireCapableBeanFactory的

    doCreateBean()方法里,有一个populateBean()里面调用了postProcessPropertyValues()。从方法名称上,我们知道创建Bean(doCreateBean)>填充Bean(populateBean)。在这个populateBean()里面有如一下这么一段代码:

    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);
        }
    }

      在for循环里面,判断每一个BeanPostProcessor ,看这个BeanPostProcessor 是否实现InstantiationAwareBeanPostProcessor这个接口,刚好,我们知道AutowiredAnnotationBeanPostProcessors实现了这个接口,重载了InstantiationAwareBeanPostProcessor的postProcessPropertyValues()方法。到此为止,我们知道Bean的Autowired的注入实现。那么我们在回到postProcessPropertyValues的findAutowiringMetadata(),从上面已经点出这个方法是找出注入元数据信息。那么它是如何查找的,看如下代码:

    private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, PropertyValues pvs) {
        // Fall back to class name as cache key, for backwards compatibility with custom callers.
        String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
        // Quick check on the concurrent map first, with minimal locking.
        InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
        if (InjectionMetadata.needsRefresh(metadata, clazz)) {
            synchronized (this.injectionMetadataCache) {
                metadata = this.injectionMetadataCache.get(cacheKey);
                if (InjectionMetadata.needsRefresh(metadata, clazz)) {
                    if (metadata != null) {
                        metadata.clear(pvs);
                    }
                    try {
                        metadata = buildAutowiringMetadata(clazz);
                        this.injectionMetadataCache.put(cacheKey, metadata);
                    }
                    catch (NoClassDefFoundError err) {
                        throw new IllegalStateException("Failed to introspect bean class [" + clazz.getName() +
                                "] for autowiring metadata: could not find class that it depends on", err);
                    }
                }
            }
        }
        return metadata;
    }

      其中的关键代码就是InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);这句话。从这里我们知道他是从this.injectionMetadataCache,这是一个Map,key为beanName,value为注入元数据InjectionMetadata。即直接从这个Map中获取,那么接下来我们就要知道这个注入信息是什么时候放入到这个缓存Map上的。从上面代码上,我们看到

    this.injectionMetadataCache.put(cacheKey, metadata);这段代码,这代码就是把注入信息放到this.injectionMetadataCache上。那么,从这里我们可以猜测,findAutowiringMetadata()这个方法肯定被调用了多次,在Bean实例化过程中。从查看代码,印证了我的想法。

      再回到AutowiredAnnotationBeanPostProcessor的BeanPostProcessor,之前我们知道他实现InstantiationAwareBeanPostProcessor这个接口。在这里,我要说AutowiredAnnotationBeanPostProcessor还实现了MergedBeanDefinitionPostProcessor这个接口,这个MergedBeanDefinitionPostProcessor接口只有一个函数postProcessMergedBeanDefinition(),该方法就是用来整合BeanDefinition。让我们自己毛估估也知道,这个对Bean内部的参数描述PropertyDescriptor也应该在专门用来整合Bean定义的这种BeanPostProcessors。从 AutowiredAnnotationBeanPostProcessor的如下代码,它果然实现postProcessMergedBeanDefinition函数。在其中调用了findAutowiringMetadata()。

    @Override
    public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
        if (beanType != null) {
            InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
            metadata.checkConfigMembers(beanDefinition);
        }
    }

      第一次调用findAutowiringMetadata()的时候,我们this.injectionMetadataCache.get()得到metadata为null,这样就会进入if{}段代码,接着 调用 buildAutowiringMetadata()从该Bean的字节码中得到注入元信息。接着我们把得到的注入源信息InjectionMetadata放到this.injectionMetadataCache上。那么我们看一下这个方法。

    private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
        LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<InjectionMetadata.InjectedElement>();
        Class<?> targetClass = clazz;
    
        do {
            final LinkedList<InjectionMetadata.InjectedElement> currElements =
                    new LinkedList<InjectionMetadata.InjectedElement>();
    
            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));
                    }
                }
            });
    
            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.getParameterTypes().length == 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);
            targetClass = targetClass.getSuperclass();
        }
        while (targetClass != null && targetClass != Object.class);
    
        return new InjectionMetadata(clazz, elements);
    }

    从上面,我们知道这个方法主要通过ReflectionUtils.doWithLocalFields()和ReflectionUtils.doWithLocalMethods()来得到源注入信息。到这里,我们可以大致知道依赖注入了。瞎 main提供一下test,大家可以通过debug知道Bean的创建流程。

    @Test
    public void testResourceInjection2() {
        DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
        AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
        bpp.setBeanFactory(bf);
        bf.addBeanPostProcessor(bpp);
        RootBeanDefinition bd1 = new RootBeanDefinition(Uss.class);
    
        bd1.setScope(RootBeanDefinition.SCOPE_SINGLETON);
        bf.registerBeanDefinition("uss", bd1);
    
        RootBeanDefinition bd2 = new RootBeanDefinition(Tss.class);
    
        bd2.setScope(RootBeanDefinition.SCOPE_SINGLETON);
        bf.registerBeanDefinition("tss", bd2);
    
    
        Uss uss = (Uss) bf.getBean("uss");
        Tss tss = (Tss) bf.getBean("tss");
    
        System.out.println(uss.getTss() == tss);
        System.out.println(tss.getUss() == uss);
    }
    public class Uss {
        private String id;
    
        @Autowired
        private Tss tss;
    
        public String getId() {
            return id;
        }
    
        public void setId(String id) {
            this.id = id;
        }
    
        public Tss getTss() {
            return tss;
        }
    
        public void setTss(Tss tss) {
            this.tss = tss;
        }
    }
    public class Tss {
        private String id;
    
        @Autowired
        private Uss uss;
    
        public String getId() {
            return id;
        }
    
        public void setId(String id) {
            this.id = id;
        }
    
        public Uss getUss() {
            return uss;
        }
    
        public void setUss(Uss uss) {
            this.uss = uss;
        }
    }

      在这里,还有一些东西没有讲的非常的清楚,第一,是我自己有些代码还不太清楚,整体把握不住,还有些要等到我看到了其他内容在和大家一起分享。

  • 相关阅读:
    Warning:detected "cgroupfs" as the Docker cgroup driver. The recommended driver is "systemd".
    Ubuntu16.04安装K8s步骤和踩坑记录【不错】
    修改主机名(/etc/hostname和/etc/hosts区别)
    Windows10内置Linux子系统初体验
    Docker Swarm常用命令
    Docker Swarm集群部署
    Docker容器CPU、memory资源限制
    Docker监控容器资源的占用情况
    修改Docker容器启动配置参数
    sort-colors——排序3种数字
  • 原文地址:https://www.cnblogs.com/liferecord/p/7281655.html
Copyright © 2020-2023  润新知