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 是父类加载器优先相反。
每天用心记录一点点。内容也许不重要,但习惯很重要!