听说 Spring Bean 的创建还有一条捷径?
@[toc] 在 Spring Bean 的创建方法中,有如下一段代码:
AbstractAutowireCapableBeanFactory#createBean:
@Override protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { //... try { // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance. Object bean = resolveBeforeInstantiation(beanName, mbdToUse); if (bean != null) { return bean; } } try { Object beanInstance = doCreateBean(beanName, mbdToUse, args); return beanInstance; } //... }
我们平时说的 Bean 的创建逻辑都是指 doCreateBean 方法中的逻辑,在松哥前面几篇文章中,凡是涉及到 Bean 的创建流程的,我说的也都是 doCreateBean 方法的流程。
但是小伙伴们注意,在 doCreateBean 方法执行之前,其实还有一个 resolveBeforeInstantiation 方法会先执行,而这个方法可能就直接产生一个 Bean 了!如果这个方法直接产生一个 Bean 了,那么 doCreateBean 方法中的逻辑就不会生效了。
那么 resolveBeforeInstantiation 方法存在的意义是什么呢?其实大家从该方法的注释上大概也能看出一些端倪出来了:
> 给 BeanPostProcessor 一个机会去创建一个代理对象,用这个代理对象来代替目标 Bean。
1. resolveBeforeInstantiation
看下面的源码小伙伴们一定要先搞清楚两个比较相似的单词,否则看到后面就乱了:
- instantiation:实例化,从 Class 到 Bean 就是实例化。
- initialization:初始化,给 Bean 做各种配置就是初始化。
搞明白这两个单词,我们来看源码。
首先我先来和小伙伴们稍微梳理一下 resolveBeforeInstantiation 方法。
AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation:
@Nullable protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) { Object bean = null; if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) { // Make sure bean class is actually resolved at this point. if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { Class<!--?--> targetType = determineTargetType(beanName, mbd); if (targetType != null) { bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName); if (bean != null) { bean = applyBeanPostProcessorsAfterInitialization(bean, beanName); } } } mbd.beforeInstantiationResolved = (bean != null); } return bean; }
小伙伴们看一下,这里有一个判断条件,mbd.isSynthetic 是判断是否是一个合成 Bean,这种一般都是 Spring 定义的,我们自定义的 Bean 一般都不属于这一类,然后后面的 hasInstantiationAwareBeanPostProcessors 方法则是判断当前是否存在 InstantiationAwareBeanPostProcessor 类型的后置处理器,如果存在,则进入到 if 分支中。
如果我们想要在 resolveBeforeInstantiation 方法中就完成 Bean 的处理,那么就需要自己提供一个 InstantiationAwareBeanPostProcessor 类型的后置处理器。
接下来会调用两个方法:
- applyBeanPostProcessorsBeforeInstantiation:从名字可以看出来,这个是在实例化之前触发的方法,所以这个方法的参数还是 Class,因为还未实例化。
- applyBeanPostProcessorsAfterInitialization:从名字可以看出来,这个是在初始化之后出发的方法,所以这个方法的参数是 Bean,因为此时已经完成了初始化了。
1.1 applyBeanPostProcessorsBeforeInstantiation
@Nullable protected Object applyBeanPostProcessorsBeforeInstantiation(Class<!--?--> beanClass, String beanName) { for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) { Object result = bp.postProcessBeforeInstantiation(beanClass, beanName); if (result != null) { return result; } } return null; }
这个地方就很简单了,就是执行 InstantiationAwareBeanPostProcessor 类型的后置处理器的 postProcessBeforeInstantiation 方法。
1.2 applyBeanPostProcessorsAfterInitialization
@Override public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor processor : getBeanPostProcessors()) { Object current = processor.postProcessAfterInitialization(result, beanName); if (current == null) { return result; } result = current; } return result; }
这个是执行 BeanPostProcessor 的 postProcessAfterInitialization 方法。
所以这块的源码其实并不难,道理很简单。
1.3 案例
松哥写一个简单的案例小伙伴们来看下。
假设我有一个 BookService,如下:
public class BookService { public void hello() { System.out.println("hello javaboy"); } }
然后我再创建一个 InstantiationAwareBeanPostProcessor 类型的后置处理器,并且重写前面提到的 postProcessBeforeInstantiation 和 postProcessAfterInitialization 方法:
public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor { @Override public Object postProcessBeforeInstantiation(Class<!--?--> beanClass, String beanName) throws BeansException { if (beanClass == BookService.class) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(beanClass); enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> { String name = method.getName(); System.out.println(name + " 方法开始执行了..."); Object invoke = proxy.invokeSuper(obj, args); System.out.println(name + " 方法执行结束了..."); return invoke; }); BookService bookService = (BookService) enhancer.create(); return bookService; } return InstantiationAwareBeanPostProcessor.super.postProcessBeforeInstantiation(beanClass, beanName); } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("bean.getClass() ========= " + bean.getClass()); return InstantiationAwareBeanPostProcessor.super.postProcessAfterInitialization(bean, beanName); } }
在 postProcessBeforeInstantiation 方法中,如果要创建的 Bean 是 BookService,则这里通过 Enhancer 来创建一个 CGLIB 的代理对象,如果是其他的 Bean 的创建,则调用父类方法即可。这样重写之后,就会导致在 1.1 小节中,获取到的 result 就是一个代理的 BookService 对象。
在 postProcessAfterInitialization 方法中,我未做任何额外处理,就是把拿到的 Bean 打印了一下,此时我们拿到手的 Bean 其实就是前面 postProcessBeforeInstantiation 方法生成的代理对象,然后这里调用父类方法去返回,实际上就是把参数 Bean 原封不动返回。
最后将这两个 Bean 注册到 Spring 容器中:
<!--?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 http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean class="org.javaboy.bean.aop2.BookService" id="bookService" /> <bean class="org.javaboy.bean.aop2.MyInstantiationAwareBeanPostProcessor" id="myInstantiationAwareBeanPostProcessor" /> </beans>
然后初始化 Spring 容器:
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("aop2.xml"); BookService bs = ctx.getBean(BookService.class); System.out.println("bs.getClass() = " + bs.getClass()); bs.hello();
最终执行结果如下:
可以看到,最终拿到的 BookService 就是一个代理对象,从源码层面来讲,这个代理对象在 resolveBeforeInstantiation 方法中就生成了,后续的 doCreateBean 方法实际上并未执行。
这就是 resolveBeforeInstantiation 方法的作用,实际上就是给 BeanPostProcessor 一个机会去创建一个代理对象,用这个代理对象来代替目标 Bean。
2. 源码实践
松哥为什么会关注到这个方法呢?
如果有小伙伴研究过 Spring AOP 源码,就会发现这个方法在处理 Spring AOP 的时候,有一个用武之地。
当我们在 Spring AOP 中,往往通过如下代码来定义切面:
@Component @Aspect @EnableAspectJAutoProxy public class LogAspect { //... }
这个类上面有一个 @Aspect 注解,那么问题来了,Spring 是如何识别出这是一个切面而非普通的 Bean 的?
答案就是在 1.1 小节中的 applyBeanPostProcessorsBeforeInstantiation 方法中,这个方法会遍历所有 InstantiationAwareBeanPostProcessor 类型的后置处理器,InstantiationAwareBeanPostProcessor 有一个子类是 AnnotationAwareAspectJAutoProxyCreator,在这个处理器中,识别出来了 LogAspect 是一个切面。
具体识别方法如下:
首先调用 AnnotationAwareAspectJAutoProxyCreator 的 postProcessBeforeInstantiation 方法(实际上是 AnnotationAwareAspectJAutoProxyCreator 的父类 AbstractAutoProxyCreator 中的方法):
@Override public Object postProcessBeforeInstantiation(Class<!--?--> beanClass, String beanName) { Object cacheKey = getCacheKey(beanClass, beanName); if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) { if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return null; } } //... }
从这个地方开始,分成了两条线:
- 如果是一个切面 Bean 的话,则执行第一个方法 isInfrastructureClass 就可以返回 true 了。
- 如果是一个普通 Bean 的话,则第一个方法会返回 false,此时就会执行第二个方法 shouldSkip(虽然该方法也会返回 false),但是该方法有一些其他的价值在里边。
2.1 切面 Bean
我们先来看 isInfrastructureClass 方法,先来看切面 Bean 是怎么处理的。
这个方法我摘了一部分出来,我们重点关注 isInfrastructureClass 方法,这个方法用来判断当前类是否是一个 Aspect:
@Override protected boolean isInfrastructureClass(Class<!--?--> beanClass) { return (super.isInfrastructureClass(beanClass) || (this.aspectJAdvisorFactory != null && this.aspectJAdvisorFactory.isAspect(beanClass))); }
这里的判断主要是两方面:
- 调用父类的方法去判断当前类是否和 AOP 相关:
protected boolean isInfrastructureClass(Class<!--?--> beanClass) { boolean retVal = Advice.class.isAssignableFrom(beanClass) || Pointcut.class.isAssignableFrom(beanClass) || Advisor.class.isAssignableFrom(beanClass) || AopInfrastructureBean.class.isAssignableFrom(beanClass); return retVal; }
这个就不用我解释了,这些都是我们在 AOP 中的老熟人了。
- 调用
aspectJAdvisorFactory.isAspect
方法去判断当前类是否包含 @Aspect 注解:
AbstractAspectJAdvisorFactory#isAspect
@Override public boolean isAspect(Class<!--?--> clazz) { return (hasAspectAnnotation(clazz) && !compiledByAjc(clazz)); } private boolean hasAspectAnnotation(Class<!--?--> clazz) { return (AnnotationUtils.findAnnotation(clazz, Aspect.class) != null); }
这代码可太好理解了,就是检查当前类是否有 @Aspect
注解。后面那个 compiledByAjc 方法是检查是否需要 ajc 编译,这个我们一般都不需要,所以,只要 hasAspectAnnotation 方法返回 true,整体上就会返回 true。
如果我们的类上包含 @Aspect
注解,那么最终就会在将当前类名加入到 advisedBeans Map 中,在 advisedBeans 这个 Map 中,key 是当前 Bean 的名称,value 则是 false 是一个标记,表示当前类不需要生成代理类。
这就是 isInfrastructureClass 方法执行的大致逻辑。
2.2 普通 Bean
如果是普通 Bean 的话,很明显 isInfrastructureClass 方法会返回 false,这就会导致 shouldSkip 方法去执行,这个方法名虽然叫 shouldSkip,但是却干了不少实事。
这个方法我会在下篇文章中和小伙伴们分享 AOP 的创建过程中再和大家详解,这里先说一句,这个方法会把各种 Aspect Bean 都收集整理起来,将来根据这些 Bean 去生成 Advisor。
好啦,这就是 resolveBeforeInstantiation 方法的作用,感兴趣的小伙伴可以自己 DEBUG 看一些哦~

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
layui-vue v2.3.8 发布,一个 Vue 3 UI 框架
更新日志: [新增] textarea 组件 autosize 属性,根据内容自适应高度。[修复] autoComplete 组件 modelValue 属性选中时不更新的问题。[文档] 补充 textarea.md 高度自适应,固定案列案例。[文档] 修正 table.md 中 max-height 与 height 属性类型描述错误。[文档] 补充 table.md 中 total-row 行统计案例。[优化] step 组件 type 属性为 primary 时的颜色为 #16baaa。[优化] step 组件 type 属性为 primary 时的颜色不跟随主题色的问题。 更多详情:http://www.layui-vue.com
- 下一篇
想学Python高级编程?必须了解这个小技巧:match-case!
大家好,这里是程序员晚枫,小破站/知乎/小红书/抖音都叫这个名字。 上次给大家分享了Python高级编程第一讲:从使用类型提示开始 ;今天分享Python高级编程第二讲:深入解析Python中switch case的使用方法。 写在前面 分享之前,先说几点注意事项: Python对switch case的支持,来自PEP634,如下图所示。 Python对switch case的支持,是通过match case实现的。语法稍有不同,作用完全一致。 经过测试,Python对switch case的支持是从3.10开始的,网上有部分文章说是3.11才开始支持是错误的。 代码演示 如下代码所示,在没有match case之前,我们通常是通过if else做匹配的。 然而,随着编程语言的不断发展,现在很多语言都已经提供了更加方便和高效的匹配方式,例如Swift语言中的switch语句。switch语句不仅可以匹配基本数据类型,还可以匹配枚举类型、元组等更加复杂的数据结构。 而且,使用switch语句的可读性和可维护性也更好,代码量更少。 因此,如果你使用的编程语言支持switch语句,建议在匹...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Docker安装Oracle12C,快速搭建Oracle学习环境
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- CentOS7安装Docker,走上虚拟化容器引擎之路
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS7设置SWAP分区,小内存服务器的救世主
- CentOS7,CentOS8安装Elasticsearch6.8.6
- Hadoop3单机部署,实现最简伪集群
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- SpringBoot2配置默认Tomcat设置,开启更多高级功能