Spring IoC 依赖查找之源码分析

Spring IoC 依赖查找之源码分析

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 是父类加载器优先相反。


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

posted on 2020-02-14 15:02  binarylei  阅读(739)  评论(0编辑  收藏  举报

导航