zoukankan      html  css  js  c++  java
  • Spring IoC 依赖查找之源码分析

    Spring IoC 依赖查找之源码分析

    [toc]

    Spring 核心编程思想目录:https://www.cnblogs.com/binarylei/p/12290153.html

    1. 名称查找

    名称查找相对类型查找到简单很多,Spring 内部缓存了所有的单例 singletonObjects,如果能命中则直接返回,否则需要新创建。

    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    
    protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
            @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
        final String beanName = transformedBeanName(name);
        Object bean;
        // 1. 从缓存中获取bean
        Object sharedInstance = getSingleton(beanName);
        if (sharedInstance != null && args == null) {
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
        // 2. 从缓存不存在,则创建新的bean
        } else {
            // createBean(beanName, mbd, args) ...
        }
        
        // 3. 类型转换
        if (requiredType != null && !requiredType.isInstance(bean)) {
            return getTypeConverter().convertIfNecessary(bean, requiredType);
        }
        return (T) bean;
    }
    

    2. 类型查找

    类型查找也可称之为类型自省,可以说是 Spring IoC 其它 API 的基础。源码分析见 Spring IoC 依赖查找之类型查找

    • 单个 Bean 类型查找
    • 集合 Bean 类型查找
    • 集合 Bean 类型名称查找

    3. 注解查找

    • Spring 3.0 获取标注类型 Bean 名称列表
      • getBeanNamesForAnnotation(Class<? extends Annotation>):遍历所有的 beanNames(包括托管 manualSingletonNames),调用 findAnnotationOnBean 查找指定的注解是否存在。
    • Spring 3.0 获取标注类型 Bean 实例列表
      • getBeansWithAnnotation(Class<? extends Annotation>):先调用 getBeanNamesForAnnotation 获取所有符合条件的 beanNames,再调用 getBean 实例化所有的 beanNames 后返回。
    • Spring 3.0 获取指定名称 + 标注类型 Bean 实例
      • findAnnotationOnBean(String, Class<? extends Annotation>):注解查找的底层 API。

    前两种查找方式源码都非常简单,就不多说了,我们重点看一下 findAnnotationOnBean 方法。

    public <A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType)
        throws NoSuchBeanDefinitionException {
    
        A ann = null;
        // 1. 获取beanName类型,可以是代理后的实例类型,导致无法获取注解
        Class<?> beanType = getType(beanName);
        if (beanType != null) {
            ann = AnnotationUtils.findAnnotation(beanType, annotationType);
        }
        // 2. BeanDefinition中beanName类型
        if (ann == null && containsBeanDefinition(beanName)) {
            BeanDefinition bd = getMergedBeanDefinition(beanName);
            if (bd instanceof AbstractBeanDefinition) {
                AbstractBeanDefinition abd = (AbstractBeanDefinition) bd;
                if (abd.hasBeanClass()) {
                    Class<?> beanClass = abd.getBeanClass();
                    if (beanClass != beanType) {
                        ann = AnnotationUtils.findAnnotation(beanClass, annotationType);
                    }
                }
            }
        }
        return ann;
    }
    

    说明: findAnnotationOnBean 代码很简单,我们需要重点说明一下:为什么要先调用 getType 方法查找,查找不到还要通过 BeanDefinition 查找,按理说,getType 就是通过 BeanDefinition 获取其类型的?

    问题的关键在于 getType 先按实例进行类型自省,再到 BeanDefinition 进行类型自省,对象类型可以不能正确获取。如果是 JDK 动态代理对象,则不能正确获取类型。更多查看:Spring IoC 依赖查找之类型自省

    案例说明

    @EnableAsync(proxyTargetClass = true)
    public class AnnotationLookupTest {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext context =
                    new AnnotationConfigApplicationContext(AnnotationLookupTest.class);
            // JDK 动态代理,导致不能从实例上获取对象类型,只能从 BeanDefinition 中获取对象类型
            Assert.assertEquals(Async.class, context.findAnnotationOnBean(
                    "beanA", Async.class).annotationType());
        }
    
        @Bean
        public BeanA beanA() {
            return new BeanAImpl();
        }
    
        @Async
        private class BeanAImpl implements BeanA {
        }
        private interface BeanA {
        }
    }
    

    说明: 在这个案例中 proxyTargetClass=true,也是采用 CGLIB 的代理方式,可以正常获取 BeanAImpl 的注解信息。改用 proxyTargetClass=false 后,采用 JDK 动态代理,却无法正常获取注解信息。因为 CGLIG 代理本质上是继承来实现的,而 JDK 动态代理则是组合方式实现的动态代理,Spring AnnotationUtils.findAnnotation(beanClass, annotationType) 方法可以递归获取父类或元注解。

    @Spring 5.1.5 Debug 了一下源码,通过 @Bean 注解的方式,实际上也是通过工厂方法获取其类型 bd.factoryMethodReturnType,而不是 bd.beanClass。此时 bd.beanClass=null,所以才会出现 Bug。在最新的版本中 @Spring 5.2.3 已经修复了这个问题,在 findAnnotationOnBean 增加了如下逻辑。

    Method factoryMethod = bd.getResolvedFactoryMethod();
    if (factoryMethod != null) {
        MergedAnnotation<A> annotation =
            MergedAnnotations.from(factoryMethod, SearchStrategy.TYPE_HIERARCHY).get(annotationType);
        if (annotation.isPresent()) {
            return annotation;
        }
    }
    

    4. 延迟查找

    Spring 5.1 Bean 延迟查找

    • getBeanProvider(Class)
    • getBeanProvider(ResolvableType)
    public <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType) {
    		return new BeanObjectProvider<T>() {
    			@Override
    			public T getObject() throws BeansException {
    				T resolved = resolveBean(requiredType, null, false);
    				if (resolved == null) {
    					throw new NoSuchBeanDefinitionException(requiredType);
    				}
    				return resolved;
    			}
                ...
            }
    }
    

    说明: BeanObjectProvider 实现了 ObjectProvider 接口,ObjectProvider 接口则是对 ObjectFactory 的扩展。getBeanProvider 内部本质上根据类型查找。getBean(Class) 也是通过 resolveBean 方法进行查找。

    5. 层次查找

    • 根据 Bean 类型查找实例列表
      • 单一类型:BeanFactoryUtils#beanOfType
      • 集合类型:BeanFactoryUtils#beansOfTypeIncludingAncestors
    • 根据 Java 注解查找名称列表
      • BeanFactoryUtils#beanNamesForTypeIncludingAncestors
    public static String[] beanNamesForTypeIncludingAncestors(ListableBeanFactory lbf, ResolvableType type) {
        Assert.notNull(lbf, "ListableBeanFactory must not be null");
        String[] result = lbf.getBeanNamesForType(type);
        if (lbf instanceof HierarchicalBeanFactory) {
            HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
            if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
                String[] parentResult = beanNamesForTypeIncludingAncestors(
                    (ListableBeanFactory) hbf.getParentBeanFactory(), type);
                result = mergeNamesWithParent(result, parentResult, hbf);
            }
        }
        return result;
    }
    

    说明: 无非是递归调用 getBeanNamesForType 或 beanOfType 方法。和 JDK 的双亲委派机制类似,只是 Spring 子容器优先,而 JDK 是父类加载器优先相反。


    每天用心记录一点点。内容也许不重要,但习惯很重要!

  • 相关阅读:
    delphi try except语句 和 try finally语句用法以及区别
    正向代理与反向代理(转)
    kbmMW功能
    problem 202,263、232、21、231
    leetcode day8
    leetcode day7
    VS2013 opencv2.4.8
    web 前端routine
    leetcode day6
    leetcode day5
  • 原文地址:https://www.cnblogs.com/binarylei/p/12307491.html
Copyright © 2011-2022 走看看