这篇文章,我们来谈一谈Spring中的属性注入 (2)

image-20200613200058693

查看这个方法的实现类我们会发现总共就这么几个类实现了MergedBeanDefinitionPostProcessor接口。实际上除了ApplicationListenerDetector之外,其余的后置处理器的逻辑都差不多。我们在这里我们主要就分析两个后置处理

ApplicationListenerDetector

AutowiredAnnotationBeanPostProcessor

ApplicationListenerDetector

首先,我们来ApplicationListenerDetector,这个类在之前的文章中也多次提到过了,它的作用是用来处理嵌套Bean的情况,主要是保证能将嵌套在Bean标签中的ApplicationListener也能添加到容器的监听器集合中去。我们先通过一个例子来感受下这个后置处理器的作用吧

配置文件:

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans "> <bean> <constructor-arg> <bean/> </constructor-arg> </bean> </beans>

示例代码:

// 事件 public class DmzEvent extends ApplicationEvent { public DmzEvent(Object source) { super(source); } } public class DmzService { OrderService orderService; public DmzService(OrderService orderService) { this.orderService = orderService; } } // 实现ApplicationListener接口 public class OrderService implements ApplicationListener<DmzEvent> { @Override public void onApplicationEvent(DmzEvent event) { System.out.println(event.getSource()); } } public class Main { public static void main(String[] args) { ClassPathXmlApplicationContext cc = new ClassPathXmlApplicationContext("application-populate.xml"); cc.publishEvent(new DmzEvent("my name is dmz")); } } // 程序运行结果,控制台打印:my name is dmz

说明OrderService已经被添加到了容器的监听器集合中。但是请注意,在这种情况下,如果要使OrderService能够执行监听的逻辑,必须要满足下面这两个条件

外部的Bean要是单例的,对于我们的例子而言就是dmzService

内嵌的Bean也必须是单例的,在上面的例子中也就是orderService必须是单例

另外需要注意的是,这种嵌套的Bean比较特殊,它虽然由Spring创建,但是确不存在于容器中,就是说我们不能将其作为依赖注入到别的Bean中。

AutowiredAnnotationBeanPostProcessor

对应源码如下:

public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) { // 找到注入的元数据,第一次是构建,后续可以直接从缓存中拿 // 注解元数据其实就是当前这个类中的所有需要进行注入的“点”的集合, // 注入点(InjectedElement)包含两种,字段/方法 // 对应的就是AutowiredFieldElement/AutowiredMethodElement InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null); // 排除掉被外部管理的注入点 metadata.checkConfigMembers(beanDefinition); }

上面代码的核心逻辑就是

找到所有的注入点,其实就是被@Autowired注解修饰的方法以及字段,同时静态的方法以及字段也会被排除

排除掉被外部管理的注入点,在后续的源码分析中我们再细说

findAutowiringMetadata // 这个方法的核心逻辑就是先从缓存中获取已经解析好的注入点信息,很明显,在原型情况下才会使用缓存 // 创建注入点的核心逻辑在buildAutowiringMetadata方法中 private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) { String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName()); InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey); // 可能我们会修改bd中的class属性,那么InjectionMetadata中的注入点信息也需要刷新 if (InjectionMetadata.needsRefresh(metadata, clazz)) { synchronized (this.injectionMetadataCache) { metadata = this.injectionMetadataCache.get(cacheKey); if (InjectionMetadata.needsRefresh(metadata, clazz)) { if (metadata != null) { metadata.clear(pvs); } // 这里真正创建注入点 metadata = buildAutowiringMetadata(clazz); this.injectionMetadataCache.put(cacheKey, metadata); } } } return metadata; } buildAutowiringMetadata // 我们应用中使用@Autowired注解标注在字段上或者setter方法能够完成属性注入 // 就是因为这个方法将@Autowired注解标注的方法以及字段封装成InjectionMetadata // 在后续阶段会调用InjectionMetadata的inject方法进行注入 private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) { List<InjectionMetadata.InjectedElement> elements = new ArrayList<>(); Class<?> targetClass = clazz; do { final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>(); // 处理所有的被@AutoWired/@Value注解标注的字段 ReflectionUtils.doWithLocalFields(targetClass, field -> { AnnotationAttributes ann = findAutowiredAnnotation(field); if (ann != null) { // 静态字段会直接跳过 if (Modifier.isStatic(field.getModifiers())) { // 省略日志打印 return; } // 得到@AutoWired注解中的required属性 boolean required = determineRequiredStatus(ann); currElements.add(new AutowiredFieldElement(field, required)); } }); // 处理所有的被@AutoWired注解标注的方法,相对于字段而言,这里需要对桥接方法进行特殊处理 ReflectionUtils.doWithLocalMethods(targetClass, method -> { // 只处理一种特殊的桥接场景,其余的桥接方法都会被忽略 Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) { return; } AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod); // 处理方法时需要注意,当父类中的方法被子类重写时,如果子父类中的方法都加了@Autowired // 那么此时父类方法不能被处理,即不能被封装成一个AutowiredMethodElement if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) { if (Modifier.isStatic(method.getModifiers())) { // 省略日志打印 return; } if (method.getParameterCount() == 0) { // 当方法的参数数量为0时,虽然不需要进行注入,但是还是会把这个方法作为注入点使用 // 这个方法最终还是会被调用 if (logger.isInfoEnabled()) { logger.info("Autowired annotation should only be used on methods with parameters: " + method); } } boolean required = determineRequiredStatus(ann); // PropertyDescriptor: 属性描述符 // 就是通过解析getter/setter方法,例如void getA()会解析得到一个属性名称为a // readMethod为getA的PropertyDescriptor, // 在《Spring官网阅读(十四)Spring中的BeanWrapper及类型转换》文中已经做过解释 // 这里不再赘述,这里之所以来这么一次查找是因为当XML中对这个属性进行了配置后, // 那么就不会进行自动注入了,XML中显示指定的属性优先级高于注解 PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz); // 构造一个对应的AutowiredMethodElement,后续这个方法会被执行 // 方法的参数会被自动注入,这里不限于setter方法 currElements.add(new AutowiredMethodElement(method, required, pd)); } }); // 会处理父类中字段上及方法上的@AutoWired注解,并且父类的优先级比子类高 elements.addAll(0, currElements); targetClass = targetClass.getSuperclass(); } while (targetClass != null && targetClass != Object.class); return new InjectionMetadata(clazz, elements); } 难点代码分析

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpsspw.html