龙空技术网

面试:Spring BeanPostProcessor原理、实际应用场景有哪些

jwfy的学习分享 870

前言:

现在我们对“dopost返回值”大约比较注意,小伙伴们都需要了解一些“dopost返回值”的相关内容。那么小编也在网上汇集了一些有关“dopost返回值””的相关知识,希望我们能喜欢,看官们一起来学习一下吧!

分享自己在Java方面的所思所想,希望你看完之后能有更多更深入的了解

之前已经就BeanPostProcessor使用方法以及其实现细节谈论过,具体可看 用BeanPostProcessor和BeanFactoryPostProcessor修改bean的属性 现在从更加抽象的角度去理解Spring的BeanPostProcessor具体是如何工作的,现在spring自身有多少bpp,如果我们有自定义的bpp需求,应该如何实现。

目录

1、BeanPostProcessor 种类

1.1、BeanPostProcessor 接口

1.2、InstantiationAwareBeanPostProcessor 接口

1.3、MergedBeanDefinitionPostProcessor 接口

2、源码学习

2.1、实例化前的提前处理

2.2、实例化后的合并

2.3、实例化后的数据填充

2.4、初始化init方法

3、Demo

1、BeanPostProcessor 种类

如上图,是在IDEA中使用control+H命令显示出来的BeanPostProcessor的继承实现类关系图,重点介绍BeanPostProcessor,InstantiationAwareBeanPostProcessor、MergedBeanDefinitionPostProcessor三个接口,后面再继续结合实际的实现类,分析spring是如何操作BeanPostProcessor对象的

1.1、BeanPostProcessor 接口

public interface BeanPostProcessor { Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException; // 在调用bean的init方法(非构造方法)前调用 // beanName 是在Spring IOC 容器的bean 名称,返回的对象需要会被直接使用 // 切记!!!默认返回bean即可,不要无缘无故返回null,会出现各种NPE的情况 Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException; // 在调用bean的init方法(非构造方法)后调用}

1.2、InstantiationAwareBeanPostProcessor 接口

public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor { Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException; // 在对象实例化之前被调用,传入的参数是类型以及bean的那么 // 如果有返回非空的对象,则意味着不需要调用doCreate操作完成对象实例化等 // 同时返回非空后会调用postProcessAfterInitialization方法 // 注意看,不是postProcessAfterInstantiation而是postProcessAfterInitialization方法 boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException; // 返回的是boolean类型,并不是和postProcessBeforeInstantiation配套使用的,在正常的实例化之后 // 主要功能是判断是否需要完成对象填充 // 如果返回false,则意味着不会完成对象属性的填充,例如@Resource导入的对象还是为null PropertyValues postProcessPropertyValues( PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException; // 提供给已经实例化的对象一种可以自定义完成对象pv属性的修改操作 // 注意不要随意返回null,一旦返回null,不会进行对象填充、对象依赖等操作}

1.3、MergedBeanDefinitionPostProcessor 接口

public interface MergedBeanDefinitionPostProcessor extends BeanPostProcessor { void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName); // 在完成bean的实例化之后,填充数据(populateBean)之前,可自定义的修改beanDefinition内容 // 名字成为merge,合并BPP对象和对于的beanDefinition的内容 // 实际上可以完成任何想任何实现的功能}

以上的三个接口基本上能够覆盖所有的BPP特定的功能点,从上面图也可以看出来,spring、springboot很多模块都有相应的对象实现,完成特定的功能。

2、源码学习

2.1、实例化前的提前处理

直接定位到AbstractAutowireCapableBeanFactory类的createBean方法,主要介绍的是InstantiationAwareBeanPostProcessor

try { Object bean = resolveBeforeInstantiation(beanName, mbdToUse); if (bean != null) { // 如果bean有用,则会直接返回,不会再继续执行doCreateBean方法 return bean; }}catch (Throwable ex) { throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName, "BeanPostProcessor before instantiation of bean failed", ex); // bean创建失败,在初始化之前错误了}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()) { // 如果存在InstantiationAwareBeanPostProcessor对象 Class<?> targetType = determineTargetType(beanName, mbd); if (targetType != null) { bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName); // 调用的是postProcessBeforeInstantiation方法 if (bean != null) { // 如果返回的数据不为null,则调用的是postProcessAfterInitialization方法 // 注意别看错了,一个是实例化Instantiation,一个是初始化Initialization bean = applyBeanPostProcessorsAfterInitialization(bean, beanName); } } } mbd.beforeInstantiationResolved = (bean != null); // 记录下是否通过提前批次的处理(如果bean不为null,则肯定是) } // 最后返回结果 return bean;}

其意思就是利用在实例化之前检查是否存在合适的InstantiationAwareBeanPostProcessor对象,去拦截某些需要被处理的bean提前完成bean的实例化过程,不会去调用init方法,也没有数据的填充,@Resource对象的引入等操作。

2.2、实例化后的合并

实例化后,仅仅是完成了对象最基础的实例化工作,还未涉及到填充数据,init方法执行等操作,主要介绍的是MergedBeanDefinitionPostProcessor

synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { try { // 合并管理bean applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", ex); } mbd.postProcessed = true; }}protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof MergedBeanDefinitionPostProcessor) { // 遍历所有的BeanPostProcessor // 筛选出属于MergedBeanDefinitionPostProcessor的对象,调用postProcessMergedBeanDefinition MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp; bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName); } }}

2.3、实例化后的数据填充

主要的操作在populateBean方法中,涉及到的也是InstantiationAwareBeanPostProcessor

再次强调InstantiationAwareBeanPostProcessor接口不仅仅是用在实例化前,在实例化之后也同样有用

boolean continueWithPropertyPopulation = true;// 设置可以继续设置pv值的boolean对象为trueif (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { // 如果是InstantiationAwareBeanPostProcessor对象 InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { // 调用postProcessAfterInstantiation // 如果返回false意味着不需要填充pv操作了,直接break continueWithPropertyPopulation = false; break; } } }}if (!continueWithPropertyPopulation) { // 上文设置为false后,直接返回,完全忽略后续的applyPropertyValues操作 return;}if (hasInstAwareBpps || needsDepCheck) { PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); if (hasInstAwareBpps) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); // 对bean对象的pv值进行处理操作 if (pvs == null) { // 如果返回为null,则直接返回 // 所以在自定义设置BPP的时候必须注意该方法的返回值 // 通过ide自动生成的对象,其默认返回值是null return; } } } } if (needsDepCheck) { // 检查对象的依赖问题 checkDependencies(beanName, mbd, filteredPds, pvs); }}

2.4、初始化init方法

在spring中是可以设置对象的init方法,在对象实现之后,在initializeBean方法中,如下的代码片段

Object wrappedBean = bean;if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); // 调用postProcessBeforeInitialization}try { invokeInitMethods(beanName, wrappedBean, mbd); // 动态代理反射invoke调用初始化方法 // 就是在这里获取到bean的init方法调用信息,invoke调用}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); // 唯一一个被调用了2次的地方的方法postProcessAfterInitialization}public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) { result = beanProcessor.postProcessBeforeInitialization(result, beanName); if (result == null) { return result; } } return result;}public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) { result = beanProcessor.postProcessAfterInitialization(result, beanName); if (result == null) { // 都有个这样的操作,返回为null,则退出 return result; } } return result;}
3、Demo
public class CustomInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor { public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException { if (beanName.equals("student")) { Student student = new Student("bpp", 20); return student; } if (beanName.equals("superStudent")) { SuperStudent studentAndTeacher = new SuperStudent(); studentAndTeacher.setName("zhangsan"); return studentAndTeacher; } return null; } public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException { return true; } public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException { //return pvs; return null; } public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; }}public class SuperStudent { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Resource private Teacher teacher; public void doInit() { System.out.println("superstudent init"); } public void doSet() { if (teacher != null) { teacher.setAge(19); teacher.setName("superStudent"); System.out.println(teacher.toString()); } else { System.out.println("teacher is null"); } } @Override public String toString() { return "SuperStudent{" + "name='" + name + '\'' + ", teacher=" + teacher + '}'; }}public static void runBPP() { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext( new String[]{"context-bpp.xml"}); SuperStudent student = (SuperStudent) applicationContext.getBean("superStudent"); Teacher teacher = (Teacher) applicationContext.getBean("teacher"); System.out.println(teacher.toString()); System.out.println(student.toString()); student.doSet(); System.out.println(teacher.toString());}

如下图几种样例的代码

返回pv为null

很清楚在SuperStudent的对象是null,并没有完成填充操作,但是调用了init方法

返回pv为原值,不作任何改变

完成了填充操作,而且这个SuperStudent的teacher对象是和Spring IOC 容器内的保持一致

设置postProcessBeforeInstantiation

SuperStudent返回输出的对象,teacher没有值,而且也没有调用其init的方法

原创推荐

「系列教程」手写RPC框架(1),看看100个线程同时调用情况如何

「系列教程」手写RPC框架(3) zookeeper如何进行服务治理的?

「面试」new String("abc")和"abc"有什么区别?反编译看看原理吧

「源码」SpringBoot启动原理,你是否清楚?

「深入」HashMap再学习,相比Java7,Java8源码你了解多少

「面试」LRU了解么?看看LinkedHashMap如何实现LRU算法

标签: #dopost返回值