在上篇博客中分享了InstantiationAwareBeanPostProcessor接口中的四个方法,分别对其进行了详细的介绍,在文末留下了一个问题,那就是postProcessProperties方法,说到此方法是用来进行属性填充的,并且引出了CommonAnnotationBeanPostProcessor类。

一、概述

CommonAnnotationBeanPostProcessor类在spring中是一个极其重要的类,它负责解析@Resource、@WebServiceRef、@EJB三个注解。这三个注解都是定义在javax.*包下的注解,属于java中的注解。既然要解析这三个属性,那么在CommonAnnotationBeanPostProcessor类中肯定要有这三个注解,下面看这段代码,

static {
try {
@SuppressWarnings("unchecked")
Class<? extends Annotation> clazz = (Class<? extends Annotation>)
ClassUtils.forName("javax.xml.ws.WebServiceRef", CommonAnnotationBeanPostProcessor.class.getClassLoader());
webServiceRefClass = clazz;
}
catch (ClassNotFoundException ex) {
webServiceRefClass = null;
} try {
@SuppressWarnings("unchecked")
Class<? extends Annotation> clazz = (Class<? extends Annotation>)
ClassUtils.forName("javax.ejb.EJB", CommonAnnotationBeanPostProcessor.class.getClassLoader());
ejbRefClass = clazz;
}
catch (ClassNotFoundException ex) {
ejbRefClass = null;
}
//添加@Resource注解
resourceAnnotationTypes.add(Resource.class);
if (webServiceRefClass != null) {
//添加@WebServiceRef注解
resourceAnnotationTypes.add(webServiceRefClass);
}
if (ejbRefClass != null) {
//添加@EJB注解
resourceAnnotationTypes.add(ejbRefClass);
}
}

上面是一个静态代码块,我们知道静态代码块在什么时候会执行,那就是在类初始化(如未加载则先加载)的时候,也就说在CommonAnnotationBeanPostProcessor类加载的时候就会执行上面的静态代码块,我们看静态代码块中的逻辑,可以看出就是向resourceAnnotationTypes中添加了三个注解,就是这个类要解析的注解所代表的接口。

在CommonAnnoatationBeanPostProcessor类中还有一个重要的属性即injectionMetaDataCache,这个属性会存储类中的被CommonAnnoatationBeanPostProcessor解析的字段或方法信息,也就是标注了三个属性的字段或方法的元信息。

二、详述

1、方法概述

下面看CommonAnnoatationBeanPostProcessor类中的方法,

上面是CommonAnnoatationBeanPostProcessor中比较重要的方法,有两个比较熟悉的方法,在前面已经分析过即postProcessBeforeInstantiation和postProcessAfterInstantiation方法。

@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
return null;
}

上面是postProcessBeforeInstantiation方法,该方法的返回值为null,即不会在bean实例化前产生一个代理对象。

@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) {
return true;
}

上面是postProcessAfterInstantiation方法,该方法的返回值为true,也就是说该类不会阻止属性的注入。

在CommonAnnoatationBeanPostProcessor中的postProcessProperties方法,

@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
try {
metadata.inject(bean, beanName, pvs);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex);
}
return pvs;
}

先看下这个方法,今天这个方法不是主角。主角是下面这个方法,

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

这个方法首先会调用父类的postProcessMergedBeanDefinition方法,然后调用findResourceMetadata方法。

CommonAnnoatationBeanPostProcessor的父类是InitDestoryAnnoatationBeanPostProcessor,在InitDestoryAnnoatationBeanPostProcessor类中postProcessMergedBeanDefinition方法主要完成的解析和初始化(@PostConstruct)和销毁(@PreDestory)相关的注解并缓存到lifecycleMetadataCache中。

CommonAnnoatationBeanPostProcessor的findResourceMetadata方法主要完成的是解析@Resource、@WebServiceRef、@EJB三个注解并缓存到injectionMetadataCache中。今天重点分析CommonAnnoatationBeanPostProcessor的postProcessMergedBeanDefinition方法。

2、postProcessMergedBeanDefinition方法

此方法是在什么时候调用的,从spring的源码中可以找到如下,在doCreateBean方法中,下面仅贴出部分代码,

synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
//2、调用beanPostProcessor即bean后置处理器MergedBeanDefinitionPostProcessor,执行其postProcessMergedBeanDefinition方法
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
}

上面的代码中调用了applyMergedBeanDefinitionPostProcessor方法,applyMergedBeanDefinitionPostProcessor方法如下,

protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof MergedBeanDefinitionPostProcessor) {
MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
}
}
}

这里会循环beanFactory中的beanPostProcessor,这里肯定会有CommonAnnoatationBeanPostProcessor后置处理器,那么就会调用到其postProcessMergedBeanDefinition方法。

postProcessMergedBeanDefinition方法如下,

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

上面已经提到先不分析其父类,那么重点分析的就是findResourceMetadata和checkConfigMembers。findResourceMetadata如下

private InjectionMetadata findResourceMetadata(String beanName, final Class<?> clazz, @Nullable 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);
}
//重要代码,将返回的metadata对象放入injectionMetadatCache缓存中,缓存key为beanName,供后续方法从缓存中取出
metadata = buildResourceMetadata(clazz);
this.injectionMetadataCache.put(cacheKey, metadata);
}
}
}
return metadata;
}

下面看buildResourceMetadata方法,

private InjectionMetadata buildResourceMetadata(final Class<?> clazz) {
if (!AnnotationUtils.isCandidateClass(clazz, resourceAnnotationTypes)) {
return InjectionMetadata.EMPTY;
} List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
Class<?> targetClass = clazz; do {
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
//判断属性上是否有注解
ReflectionUtils.doWithLocalFields(targetClass, field -> {
if (webServiceRefClass != null && field.isAnnotationPresent(webServiceRefClass)) {
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("@WebServiceRef annotation is not supported on static fields");
}
currElements.add(new WebServiceRefElement(field, field, null));
}
else if (ejbRefClass != null && field.isAnnotationPresent(ejbRefClass)) {
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("@EJB annotation is not supported on static fields");
}
currElements.add(new EjbRefElement(field, field, null));
}
else if (field.isAnnotationPresent(Resource.class)) {
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("@Resource annotation is not supported on static fields");
}
if (!this.ignoredResourceTypes.contains(field.getType().getName())) {
currElements.add(new ResourceElement(field, field, null));
}
}
});
//判断方法上是否有注解
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
if (webServiceRefClass != null && bridgedMethod.isAnnotationPresent(webServiceRefClass)) {
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("@WebServiceRef annotation is not supported on static methods");
}
if (method.getParameterCount() != 1) {
throw new IllegalStateException("@WebServiceRef annotation requires a single-arg method: " + method);
}
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new WebServiceRefElement(method, bridgedMethod, pd));
}
else if (ejbRefClass != null && bridgedMethod.isAnnotationPresent(ejbRefClass)) {
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("@EJB annotation is not supported on static methods");
}
if (method.getParameterCount() != 1) {
throw new IllegalStateException("@EJB annotation requires a single-arg method: " + method);
}
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new EjbRefElement(method, bridgedMethod, pd));
}
else if (bridgedMethod.isAnnotationPresent(Resource.class)) {
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("@Resource annotation is not supported on static methods");
}
Class<?>[] paramTypes = method.getParameterTypes();
if (paramTypes.length != 1) {
throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);
}
if (!this.ignoredResourceTypes.contains(paramTypes[0].getName())) {
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new ResourceElement(method, bridgedMethod, pd));
}
}
}
}); elements.addAll(0, currElements);
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class); return InjectionMetadata.forElements(elements, clazz);
}

此方法较长,不过主要完成了两件事,分别从属性和方法上判断是否有@Resource、@WebServiceRef、@EJB注解,如果存在则放入injectionMetadataCache中。从这里还可以看出这三个注解可以用在方法和属性上。

到这里CommonAnnoatationBeanPostProcessor类的postProcssMergedBeanDefinition方法已经分析完毕,其作用(不包含其父类的作用)分别从解析类中的@Resource、@WebServiceRef、@EJB三个注解,并放入injectionMetadataCache缓存中,以便postProcessProperties方法使用。

三、使用场景

从上面的分析中已经知道了CommonAnnoatationBeanProcessor的postProcessMergedBeanDefinition方法的作用。就是解析@Resource、@WebServiceRef、@EJB三个注解并缓存元数据信息。下面会分析如何使用缓存在injectionMetadataCache中的信息,也就是postProcessPerties方法的逻辑。

原创不易,有不正之处欢迎指正。

最新文章

  1. SQL2005 表分区亲测
  2. 如何让{dede:channel}有子栏目显示子栏目,无子栏目不显示同级栏目
  3. C++中 OOP相关的类型转换
  4. HDU3359 Kind of a Blur(高斯消元)
  5. Codeforces 650B Image Preview
  6. SharePoint 2013 运行在IIS 应用32位错误
  7. .net的 async 和 await
  8. iOS程序的完整启动过程(有storyboard)
  9. (转)Discuz!NT图文安装教程
  10. ios中的关键词@property @synthesize
  11. c# Web.config中 windows连接数据库
  12. Spring Boot 2.x(十一):AOP实战--打印接口日志
  13. 【XSY2851】蛋糕 数学
  14. 离线eclipse添加web工程
  15. Thinkphp5.1 模板路径报错
  16. 记一次 windows server 2012R2 上安装 MSSQL2005 及网站发布
  17. 简易Samba服务器配置
  18. oo作业总结(四)
  19. php请求php
  20. Ubuntu 14.04 禁用ipv6

热门文章

  1. R的plotmath
  2. nuxt.js如何实现同级目录下建多个动态路由,并将链接设置.html后缀
  3. Asp.Net Core 3.1 获取不到Post、Put请求的内容 System.NotSupportedException Specified method is not supported
  4. 如何区分前端BUG和后台BUG?
  5. 华为云+NextCloud(私人云盘搭建)
  6. 滑动窗口-Moving Stones Until Consecutive II
  7. TensorFlow 趣题
  8. 构建一个简单的 Google Dialogflow 聊天机器人【上】
  9. 如何实现浏览器的Console功能
  10. 面向对象(OO)第一阶段学习总结