当前位置: 首页 > news >正文

Sping源码(九)—— Bean的初始化(非懒加载)—mergeBeanDefinitionPostProcessor

序言

前几篇文章详细介绍了Spring中实例化Bean的各种方式,其中包括采用FactoryBean的方式创建对象、使用反射创建对象、自定义BeanFactoryPostProcessor以及构造器方式创建对象。

创建对象
这里再来简单回顾一下对象的创建,不知道大家有没有这样一个疑问,为什么创建对象之前要获取实例策略的?意义在哪?
在这里插入图片描述
因为我们在createBeanInstance()中调用instantiateBean()方法进行类的实例化创建时,会有很多种选择 。根据构造器、工厂方法、参数…等等,而其中一部分是采用Cglib动态代理的方式实例化,其中一部分就是普通的Simple实例化。
在这里插入图片描述

SimpleInstantiationStrategy
而我们看到SimpleInstantiationStrategy类中方法就会方法,类中共有3个instantiate()同名方法,而每个方法传递的参数也大不一样,根据参数就可判断出是根据不同的条件来创建对象(构造器、工厂方法…)

public class SimpleInstantiationStrategy implements InstantiationStrategy {@Overridepublic Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {// 省略方法逻辑}protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {throw new UnsupportedOperationException("Method Injection not supported in SimpleInstantiationStrategy");}@Overridepublic Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,final Constructor<?> ctor, Object... args) {// 省略方法逻辑}protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName,BeanFactory owner, @Nullable Constructor<?> ctor, Object... args) {throw new UnsupportedOperationException("Method Injection not supported in SimpleInstantiationStrategy");}@Overridepublic Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,@Nullable Object factoryBean, final Method factoryMethod, Object... args) {// 省略方法体}
}

而继承CglibSubclassingInstantiationStrategySimpleInstantiationStrategy,因为在Simple类中已经对instantiate()进行了三种不同的实现,所以在Cglib中没对instantiate()做额外处理,而是实现了instantiateWithMethodInjection方法。
但底层调用的也是instantiate(),并根据构造器来判断具体的实现方式。

public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationStrategy {@Overrideprotected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {return instantiateWithMethodInjection(bd, beanName, owner, null);}@Overrideprotected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,@Nullable Constructor<?> ctor, Object... args) {// Must generate CGLIB subclass...return new CglibSubclassCreator(bd, owner).instantiate(ctor, args);}// 省略部分源码public Object instantiate(@Nullable Constructor<?> ctor, Object... args) {//根据beanDefinition创建一个动态生成的子类Class<?> subclass = createEnhancedSubclass(this.beanDefinition);Object instance;// 如果构造器等于空,那么直接通过反射来实例化对象if (ctor == null) {instance = BeanUtils.instantiateClass(subclass);}else {try {// 通过cglib对象来根据参数类型获取对应的构造器Constructor<?> enhancedSubclassConstructor = subclass.getConstructor(ctor.getParameterTypes());// 通过构造器来获取对象instance = enhancedSubclassConstructor.newInstance(args);}// 省略部分源码}
}	

所以Spring中获取实例话策略后,共5种创建对象的方式。并且Spring中对象的创建也并不都是采用Cglib动态代理。
在这里插入图片描述
回顾完了对象的创建,我们顺着代码的逻辑继续向下执行。
现在对象创建了,但是我们还不知道对象的初始化(init)和销毁(destroy)方法是什么。接下来就是对这两个方法做处理。

测试类

Person类中省略了get、set和构造器方法。
而为什么不用@Init注解来表示初始化方法?
因为Spring中并没有提供,下面的两个注解是Java提供的元注解,优先于@Init方法执行。

public class Person {private String name;private int age;@PostConstructpublic void init(){System.out.println("执行init方法");}@PreDestroypublic void destroy(){System.out.println("执行destroy方法");}
}

mergePostProcessor.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"><context:component-scan base-package="org.springframework.mergePostProcessor"></context:component-scan><bean id="person" class="org.springframework.mergePostProcessor.Person"><property name="name" value="张三"></property><property name="age" value="18"></property></bean>
</beans>

main

public class TestMergePostProcessor {public static void main(String[] args) {ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("mergePostProcessor.xml");Person person = ac.getBean("person", Person.class);ac.close();}
}

mergeBeanDefinitionPostProcessor

让我们把视线拉回到doCreateBean()中。 此时我们已经通过createBeanInstance()完成了对象的实例化操作。

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {// Instantiate the bean.//这个beanWrapper是用来持有创建出来的bean对象的BeanWrapper instanceWrapper = null;//如果是单例对象,从factoryBeanInstanceCache缓存中移除该信息if (mbd.isSingleton()) {// 如果是单例对象,从factoryBean实例缓存中移除当前bean定义信息instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);}// 没有就创建实例if (instanceWrapper == null) {// 根据执行bean使用对应的策略创建新的实例,如,工厂方法,构造函数主动注入、简单初始化instanceWrapper = createBeanInstance(beanName, mbd, args);}//从包装类wrapped中获取原始的实例Object bean = instanceWrapper.getWrappedInstance();//获取具体bean对象的classClass<?> beanType = instanceWrapper.getWrappedClass();//如果不是NullBean类型,则修改目标类型if (beanType != NullBean.class) {mbd.resolvedTargetType = beanType;}// Allow post-processors to modify the merged bean definition.//允许postProcessor修改合并的BeanDefinitionsynchronized (mbd.postProcessingLock) {// 如果没有执行过下面方法。if (!mbd.postProcessed) {try {applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);}catch (Throwable ex) {throw new BeanCreationException;}mbd.postProcessed = true;}}// 省略部分源码}	

获取BeanPostProcess,并找到MergedBeanDefinitionPostProcessor类型的执行postProcessMergedBeanDefinition方法。

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);}}}

先来看眼postProcessMergedBeanDefinition的继承关系。
在这里插入图片描述
postProcessMergedBeanDefinition()方法的实现有很多,但是其中都有一个特点,就是都是AnnotationBeanPostProcessor后缀,所以都是对注解的处理。
而当我们Person对象加载、解析<context:component-scan>标签时,就会将CommonAnnotationBeanPostProcessor注入到工厂中。所以在上面getBeanPostProcessors()调用时,会获取到CommonAnnotationBeanPostProcessor并执行postProcessMergedBeanDefinition
在这里插入图片描述
关于<context:component-scan>标签解析时如何进行组件的注册可看这篇帖子context: component-scan标签如何扫描、加载Bean。

CommonAnnotationBeanPostProcessor
当我们CommonAnnotationBeanPostProcessor实例创建时,构造方法中会将PostConstruct.classPreDestroy.class设置到属性中。

public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBeanPostProcessorimplements InstantiationAwareBeanPostProcessor, BeanFactoryAware, Serializable {public CommonAnnotationBeanPostProcessor() {setOrder(Ordered.LOWEST_PRECEDENCE - 3);setInitAnnotationType(PostConstruct.class);setDestroyAnnotationType(PreDestroy.class);ignoreResourceType("javax.xml.ws.WebServiceContext");}public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {//处理@PostConstruct 和 @PreDestroy 注解super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);metadata.checkConfigMembers(beanDefinition);}}	

方法首先会调用父类中InitDestroyAnnotationBeanPostProcessorpostProcessMergedBeanDefinition方法。

postProcessMergedBeanDefinition
获取生命周期元数据信息并保存

public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {//获取生命周期元数据信息并保存LifecycleMetadata metadata = findLifecycleMetadata(beanType);metadata.checkConfigMembers(beanDefinition);}

findLifecycleMetadata
依然是先从缓存中获取,获取不到则构建LifecycleMetadata对象并放到缓存中。

private LifecycleMetadata findLifecycleMetadata(Class<?> clazz) {//如果生命周期元数据缓存为空,则直接构建生命周期元数据 (默认创建了一个ConCurrentHashMap)if (this.lifecycleMetadataCache == null) {// Happens after deserialization, during destruction...return buildLifecycleMetadata(clazz);}// Quick check on the concurrent map first, with minimal locking.//快速检查生命周期元数据缓存,如果没有,则构建生命周期元数据LifecycleMetadata metadata = this.lifecycleMetadataCache.get(clazz);//缓存中不存在if (metadata == null) {//双层锁,防止多线程重复执行synchronized (this.lifecycleMetadataCache) {//再次从缓存中获取metadata = this.lifecycleMetadataCache.get(clazz);if (metadata == null) {//构建生命周期元数据metadata = buildLifecycleMetadata(clazz);//将构建好的数据放入缓存中this.lifecycleMetadataCache.put(clazz, metadata);}return metadata;}}return metadata;}

在这里插入图片描述
buildLifecycleMetadata
将 init 方法和 destroy 方法分类,如果父类中也包含注解,则循环处理。
因为会优先执行父类初始化方法, 所以 init 放入集合时,会放在 index = 0 的位置。

private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) {//是否包含@PostConstruct注解和@PreDestroy注解if (!AnnotationUtils.isCandidateClass(clazz, Arrays.asList(this.initAnnotationType, this.destroyAnnotationType))) {return this.emptyLifecycleMetadata;}//声明初始化方法集合List<LifecycleElement> initMethods = new ArrayList<>();//声明销毁方法集合List<LifecycleElement> destroyMethods = new ArrayList<>();//目标类型Class<?> targetClass = clazz;do {//保存当前正在处理的方法final List<LifecycleElement> currInitMethods = new ArrayList<>();final List<LifecycleElement> currDestroyMethods = new ArrayList<>();// 反射获取当前类中的所有方法并依次对其调用第二个参数的lambda表达式ReflectionUtils.doWithLocalMethods(targetClass, method -> {//当前方法包含initAnnotationType注解时(@PostConstruct)if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {//封装成LifecycleElement对象,添加到currInitMethods集合中LifecycleElement element = new LifecycleElement(method);currInitMethods.add(element);}// 当前方法包含destroyAnnotationType注解时(@PreDestroy)if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) {//封装成LifecycleElement对象,添加到currDestroyMethods集合中currDestroyMethods.add(new LifecycleElement(method));}});//每次都将currInitMethods集合中的元素添加到initMethods集合最前面// 因为可能父类中也包含@PostConstruct注解的方法,所以需要先执行父类的方法initMethods.addAll(0, currInitMethods);//放入destroyMethod的总集合中destroyMethods.addAll(currDestroyMethods);//获取父类targetClass = targetClass.getSuperclass();}//如果父类不是Object.class,则继续循环while (targetClass != null && targetClass != Object.class);//如果集合为空,则返回一个空的LifecycleMetadata对象,否则封装并返回一个LifecycleMetadata对象return (initMethods.isEmpty() && destroyMethods.isEmpty() ? this.emptyLifecycleMetadata :new LifecycleMetadata(clazz, initMethods, destroyMethods));}

在这里插入图片描述
而当我们处理完后会进行检查,并放入beanDefinition中,等待执行。

public void checkConfigMembers(RootBeanDefinition beanDefinition) {Set<LifecycleElement> checkedInitMethods = new LinkedHashSet<>(this.initMethods.size());for (LifecycleElement element : this.initMethods) {String methodIdentifier = element.getIdentifier();if (!beanDefinition.isExternallyManagedInitMethod(methodIdentifier)) {// 注册初始化调用方法beanDefinition.registerExternallyManagedInitMethod(methodIdentifier);checkedInitMethods.add(element);}}Set<LifecycleElement> checkedDestroyMethods = new LinkedHashSet<>(this.destroyMethods.size());for (LifecycleElement element : this.destroyMethods) {String methodIdentifier = element.getIdentifier();if (!beanDefinition.isExternallyManagedDestroyMethod(methodIdentifier)) {// 注册销毁调用方法beanDefinition.registerExternallyManagedDestroyMethod(methodIdentifier);checkedDestroyMethods.add(element);}}this.checkedInitMethods = checkedInitMethods;this.checkedDestroyMethods = checkedDestroyMethods;}

执行完成后,beanDefinition中也有了待执行的初始化方法和销毁方法。
在这里插入图片描述
因为整个Bean的实例化、加载过程只有BeanDefinition是伴随始终的,所以处理完之后要设置到BD中,也正好印证了方法上的那个注释:允许postProcessor修改合并的BeanDefinition

相关文章:

  • 巴图制自动化Profinet协议转Modbus协议模块连接PLC和电表通信
  • opencv 处理图像去噪的几种方法
  • Spring系统学习-什么是AOP?为啥使用AOP?
  • 将一个立方体对象的值赋给另一个立方体对象
  • 理解论文笔记:基于贝叶斯网络和最大期望算法的可维护性研究
  • ubuntu修改磁盘挂载目录名
  • 网络物理隔离
  • C++ 运算符的优先级和结合性表
  • 停车场车牌识别计费系统,用Python如何实现?
  • 无法定位程序输入点Z9 qt assertPKcS0i于动态链接库F:\code\projects\06_algorithm\main.exe
  • react antd表格翻页时记录勾选状态
  • Hack The Box-Editorial
  • C++ 和C#的差别
  • 手写一个类似@RequestParam的注解(用来接收请求体的参数)
  • FlinkCDC 数据同步优化及常见问题排查
  • 【干货分享】SpringCloud微服务架构分布式组件如何共享session对象
  • 5分钟即可掌握的前端高效利器:JavaScript 策略模式
  • C++类的相互关联
  • ES10 特性的完整指南
  • ES6简单总结(搭配简单的讲解和小案例)
  • golang中接口赋值与方法集
  • java 多线程基础, 我觉得还是有必要看看的
  • KMP算法及优化
  • macOS 中 shell 创建文件夹及文件并 VS Code 打开
  • MySQL Access denied for user 'root'@'localhost' 解决方法
  • Redis的resp协议
  • SpiderData 2019年2月13日 DApp数据排行榜
  • spring-boot List转Page
  • Wamp集成环境 添加PHP的新版本
  • webpack项目中使用grunt监听文件变动自动打包编译
  • 闭包,sync使用细节
  • 给第三方使用接口的 URL 签名实现
  • 删除表内多余的重复数据
  • NLPIR智能语义技术让大数据挖掘更简单
  • # 消息中间件 RocketMQ 高级功能和源码分析(七)
  • ###51单片机学习(2)-----如何通过C语言运用延时函数设计LED流水灯
  • $redis-setphp_redis Set命令,php操作Redis Set函数介绍
  • (Bean工厂的后处理器入门)学习Spring的第七天
  • (Matalb时序预测)WOA-BP鲸鱼算法优化BP神经网络的多维时序回归预测
  • (安卓)跳转应用市场APP详情页的方式
  • (附源码)计算机毕业设计大学生兼职系统
  • (转)Android中使用ormlite实现持久化(一)--HelloOrmLite
  • (转)一些感悟
  • (最完美)小米手机6X的Usb调试模式在哪里打开的流程
  • . NET自动找可写目录
  • .chm格式文件如何阅读
  • .NET Core中的去虚
  • .Net mvc总结
  • .NET 分布式技术比较
  • .NET 使用 ILMerge 合并多个程序集,避免引入额外的依赖
  • .NET 直连SAP HANA数据库
  • .net程序集学习心得
  • .net开发时的诡异问题,button的onclick事件无效
  • /etc/fstab和/etc/mtab的区别
  • [20171102]视图v$session中process字段含义