【Spring注解驱动开发】面试官再问你BeanPostProcessor的执行流程,就把这篇文章甩给他!

4.可以使用BeanPostProcessor

/**

  • 后置处理器:初始化前后进行处理工作

  • 将后置处理器加入到容器中

  • 在bean初始化前后进行一些处理工作;

  • postProcessBeforeInitialization:在初始化之前工作

  • postProcessAfterInitialization:在初始化之后工作

*/

@Component

public class MyBeanPostProcessor implements BeanPostProcessor,Ordered {

@Override

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

System.out.println(“postProcessBeforeInitialization…”+beanName+“=>”+bean);

return bean;

}

@Override

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

// TODO Auto-generated method stub

System.out.println(“postProcessAfterInitialization…”+beanName+“=>”+bean);

return bean;

}

@Override

public int getOrder() {

return 3;

}

}

通过这几种方式,我们就可以对bean的整个生命周期进行控制:

  • 从bean的实例化:调用bean的构造方法,我们可以在bean的无参构造方法中执行相应的逻辑。

  • bean的初始化:在初始化时,可以通过BeanPostProcessor的postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法进行拦截,执行自定义的逻辑;通过@PostConstruct注解、InitializingBean和init-method来指定bean初始化前后执行的方法,执行自定义的逻辑。

  • bean的销毁:可以通过@PreDestroy注解、DisposableBean和destroy-method来指定bean在销毁前执行的方法,指执行自定义的逻辑。

所以,通过上述方式,我们可以控制Spring中bean的整个生命周期。

BeanPostProcessor源码解析


如果想深刻理解BeanPostProcessor的工作原理,那就不得不看下相关的源码,我们可以在MyBeanPostProcessor类的postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法中打上断点来进行调试。如下所示。

在这里插入图片描述

随后,我们以Debug的方式来运行BeanLifeCircleTest类的testBeanLifeCircle04()方法,运行后的效果如下所示。

在这里插入图片描述

可以看到,程序已经运行到MyBeanPostProcessor类的postProcessBeforeInitialization()方法中,在IDEA的左下角我们可以清晰的看到方法的调用栈,如下所示。

在这里插入图片描述

通过这个方法调用栈,我们可以详细的分析从运行BeanLifeCircleTest类的testBeanLifeCircle04()方法开始,到进入MyBeanPostProcessor类的postProcessBeforeInitialization()方法的执行流程。只要我们在IDEA的方法调用栈中找到BeanLifeCircleTest类的testBeanLifeCircle04()方法,依次分析方法调用栈中在BeanLifeCircleTest类的testBeanLifeCircle04()方法上面位置的方法,即可了解整个方法调用栈的过程。要想定位方法调用栈中的方法,只需要在IDEA的方法调用栈中单击相应的方法即可。

注意:方法调用栈是先进后出的,也就是说,最先调用的方法会最后退出,每调用一个方法,JVM会将当前调用的方法放入栈的栈顶,方法退出时,会将方法从栈顶的位置弹出。有关方法调用的具体细节内容,后续会在【JVM】专栏详细介绍,这里,小伙伴们就先了解到此即可。

接下来,我们在IDEA的方法调用栈中,找到BeanLifeCircleTest类的testBeanLifeCircle04()方法并单击,此时IDEA的主界面会定位到BeanLifeCircleTest类的testBeanLifeCircle04()方法,如下所示。

在这里插入图片描述

在BeanLifeCircleTest类的testBeanLifeCircle04()方法中,首先通过new实例对象的方式创建了一个IOC容器。接下来,通过IDEA的方法调用栈继续分析,接下来,进入的是AnnotationConfigApplicationContext类的构造方法。

在这里插入图片描述

在AnnotationConfigApplicationContext类的构造方法中会调用refresh()方法。我们跟进方法调用栈,如下所示。

在这里插入图片描述

可以看到,方法的执行定位到AbstractApplicationContext类的refresh()方法中的如下代码行。

finishBeanFactoryInitialization(beanFactory);

这行代码的作用就是:初始化所有的(非懒加载的)单实例bean对象。

我们继续跟进方法调用栈,如下所示。

在这里插入图片描述

此时,方法的执行定位到AbstractApplicationContext类的finishBeanFactoryInitialization()方法的如下代码行。

beanFactory.preInstantiateSingletons();

这行代码的作用同样是:初始化所有的(非懒加载的)单实例bean。

我们继续跟进方法调用栈,如下所示。

在这里插入图片描述

可以看到,方法的执行定位到DefaultListableBeanFactory的preInstantiateSingletons()方法的最后一个else分支调用的getBean()方法上。继续跟进方法调用栈,如下所示。

在这里插入图片描述

此时方法定位到AbstractBeanFactory类中的getBean()方法中,在getBean()方法中,又调用了doGetBean()方法,通过方法调用栈我们可以得知方法的执行定位到AbstractBeanFactory类中的doGetBean()方法的如下代码段。

在这里插入图片描述

可以看到,在Spring内部是通过getSingleton()来获取单实例bean的,我们继续跟进方法调用栈,如下所示。

在这里插入图片描述

此时,方法定位到了DefaultSingletonBeanRegistry了类的getSingleton()方法的如下代码行。

singletonObject = singletonFactory.getObject();

继续跟进方法调用栈,如下所示。

在这里插入图片描述

此时,方法会定位到AbstractBeanFactory类的doGetBean()方法中的如下代码行。

return createBean(beanName, mbd, args);

也就是说,当第一次获取单实例bean时,由于单实例bean还未创建,Spring会调用createBean()方法来创建单实例bean。继续跟进方法调用栈,如下所示。

在这里插入图片描述

可以看到,方法的执行定位到AbstractAutowireCapableBeanFactory类的createBean()方法的如下代码行。

Object beanInstance = doCreateBean(beanName, mbdToUse, args);

可以看到,Spring中创建单实例bean调用的是doCreateBean()方法。没错,继续跟进方法调用栈,如下所示。

在这里插入图片描述

方法的执行已经定位到AbstractAutowireCapableBeanFactory类的doCreateBean()方法的如下代码行。

exposedObject = initializeBean(beanName, exposedObject, mbd);

继续跟进方法调用栈,如下所示。

在这里插入图片描述

方法的执行定位到AbstractAutowireCapableBeanFactory类的initializeBean()方法的如下代码行。

wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);

小伙伴们需要重点留意一下这个applyBeanPostProcessorsBeforeInitialization()方法。回过头来我们再来看AbstractAutowireCapableBeanFactory类的doCreateBean()方法中的如下代码行。

exposedObject = initializeBean(beanName, exposedObject, mbd);

没错,在AbstractAutowireCapableBeanFactory类的doCreateBean()方法中调用的initializeBean()方法中调用了后置处理器的逻辑。小伙伴们需要注意一下,在AbstractAutowireCapableBeanFactory类的doCreateBean()方法中调用的initializeBean()方法之前,调用了一个populateBean()方法,代码行如下所示。

populateBean(beanName, mbd, instanceWrapper);

我们点到这个populateBean()方法中,看下这个方法执行了哪些逻辑,如下所示。

populateBean()方法同样是AbstractAutowireCapableBeanFactory类中的方法,populateBean()方法的代码比较多,其实逻辑非常简单,populateBean()方法做的工作就是为bean的属性赋值。也就是说,在Spring中会先调用populateBean()方法为属性赋好值,然后再调用initializeBean()方法。接下来,我们好好分析下initializeBean()方法,为了方便,我将Spring中AbstractAutowireCapableBeanFactory类的initializeBean()方法的代码拿出来,如下所示。

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {

if (System.getSecurityManager() != null) {

AccessController.doPrivileged((PrivilegedAction) () -> {

invokeAwareMethods(beanName, bean);

return null;

}, getAccessControlContext());

}

else {

invokeAwareMethods(beanName, bean);

}

Object wrappedBean = bean;

if (mbd == null || !mbd.isSynthetic()) {

wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);

}

try {

invokeInitMethods(beanName, wrappedBean, mbd);

}

catch (Throwable ex) {

throw new BeanCreationException(

(mbd != null ? mbd.getResourceDescription() : null),

beanName, “Invocation of init method failed”, ex);

}

if (mbd == null || !mbd.isSynthetic()) {

wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

}

return wrappedBean;

}

在initializeBean()方法中,调用了invokeInitMethods()方法,代码行如下所示。

invokeInitMethods(beanName, wrappedBean, mbd);

invokeInitMethods()方法的作用就是:执行初始化方法,这些初始化方法包括我们之前讲的: 在xml文件中的标签中使用init-method属性指定的初始化方法;在@Bean注解中使用initMehod属性指定的方法;使用@PostConstruct注解标注的方法;实现InitializingBean接口的方法等。

在调用invokeInitMethods()方法之前,Spring调用了applyBeanPostProcessorsBeforeInitialization()方法,代码行如下所示。

wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);

在调用invokeInitMethods()方法之后,Spring调用了applyBeanPostProcessorsAfterInitialization()方法,如下所示。

wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

这里,我们先来看看applyBeanPostProcessorsBeforeInitialization()方法中具体执行了哪些逻辑,applyBeanPostProcessorsBeforeInitialization()方法位于AbstractAutowireCapableBeanFactory类中,源码如下所示。

@Override

public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)

throws BeansException {

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

即使是面试跳槽,那也是一个学习的过程。只有全面的复习,才能让我们更好的充实自己,武装自己,为自己的面试之路不再坎坷!今天就给大家分享一个Github上全面的Java面试题大全,就是这份面试大全助我拿下大厂Offer,月薪提至30K!

我也是第一时间分享出来给大家,希望可以帮助大家都能去往自己心仪的大厂!为金三银四做准备!
一共有20个知识点专题,分别是:

Dubbo面试专题

JVM面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

Java并发面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

Kafka面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

MongDB面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

MyBatis面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

MySQL面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

Netty面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

RabbitMQ面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

Redis面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

Spring Cloud面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

SpringBoot面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

zookeeper面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

常见面试算法题汇总专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

计算机网络基础专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

设计模式专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
53)]

Redis面试专题

[外链图片转存中…(img-8pzdOOdl-1713543702355)]

Spring Cloud面试专题

[外链图片转存中…(img-3ViekMLs-1713543702356)]

SpringBoot面试专题

[外链图片转存中…(img-cKiPr8Lm-1713543702357)]

zookeeper面试专题

[外链图片转存中…(img-kD8Qnstt-1713543702358)]

常见面试算法题汇总专题

[外链图片转存中…(img-UnhxVgJf-1713543702359)]

计算机网络基础专题

[外链图片转存中…(img-YlXXGp4C-1713543702360)]

设计模式专题

[外链图片转存中…(img-HvR80X5E-1713543702361)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值