做旅游的网站 优帮云,网站设计风格分析,工业设计考研,提高网站安全性文章目录 功能源码解析执行 功能
Spring中存在很多回调#xff0c;但是执行他们的时机都不相同#xff0c;也许大家用的最多的是InitializingBean.afterPropertiesSet#xff0c;这个方法的作用如名称一样#xff0c;是bean初始化后执行的一个回调操作#xff0c;而PostC… 文章目录 功能源码解析执行 功能
Spring中存在很多回调但是执行他们的时机都不相同也许大家用的最多的是InitializingBean.afterPropertiesSet这个方法的作用如名称一样是bean初始化后执行的一个回调操作而PostConstruct是initMethod初始化回调方法它是在afterPropertiesSet之前执行的并且可以有多个PostConstruct和PreDestory所以它的功能会更强大一点。
但需要注意的是多个PostConstruct方法的顺序是父类内从先到后然后子类内从先到后多个PreDestory方法的顺序是子类内从先到后父类内从先到后。
另外一个点这里我说它是initMethod是因为Spring本身对这个方法解析后的命名就叫initMethod保存在InitDestroyAnnotationBeanPostProcessor中真正意义上的initMethod是保存在BeanDefinition里的并且只有一个所以这个要区分一下。
如下示例
Component
public class CustomConfig7 {PostConstructpublic void t() {System.out.println(customConfig7 init);}PostConstructpublic void t2() {System.out.println(customConfig7 init2);}PreDestroypublic void d() {System.out.println(customConfig7 destroy);}PreDestroypublic void d2() {System.out.println(customConfig7 destroy2);}
}
源码
它的解析和执行过程比较简单很快就可以看完。
解析
位置org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#postProcessMergedBeanDefinition 这里它是调用了父类的方法postProcessMergedBeanDefinition进行解析的 下面findResourceMetadata是依赖注入的注入点解析。
private LifecycleMetadata findLifecycleMetadata(Class? clazz) {// lifecycleMetadataCache 初始化时赋了初始值所以这里不会走if (this.lifecycleMetadataCache null) {return buildLifecycleMetadata(clazz);}// 从缓存种获取// 双重判断LifecycleMetadata metadata this.lifecycleMetadataCache.get(clazz);if (metadata null) {synchronized (this.lifecycleMetadataCache) {metadata this.lifecycleMetadataCache.get(clazz);if (metadata null) {// 解析classmetadata buildLifecycleMetadata(clazz);this.lifecycleMetadataCache.put(clazz, metadata);}return metadata;}}return metadata;}
这里会看到synchronizedSpring创建bean也就是bean的生命周期这个过程时单线程的但它这里加了锁这个问题作为底层框架要保证线程安全因为在业务场景下并不都是单线程的比如
你定义了一个bean简称A同时你实现了接口InitializingBean在回调方法时你开启了一个线程B这个线程B可能做数据查询数据缓存等那么他需要redisdatasourcesqlSession这些那么它也是从beanFactory获取bean这时Spring也在getBean线程B和Spring就已经并行了就是应用本身是多线程的作为底层的框架需要保证线程安全。
那接下来我们看一下构建方法
位置org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#buildLifecycleMetadata
private LifecycleMetadata buildLifecycleMetadata(final Class? clazz) {// 进行过滤// initAnnotationType - PostConstruct// destroyAnnotationType - PreDestroyif (!AnnotationUtils.isCandidateClass(clazz, Arrays.asList(this.initAnnotationType, this.destroyAnnotationType))) {return this.emptyLifecycleMetadata;}ListLifecycleElement initMethods new ArrayList();ListLifecycleElement destroyMethods new ArrayList();Class? targetClass clazz;do {final ListLifecycleElement currInitMethods new ArrayList();final ListLifecycleElement currDestroyMethods new ArrayList();
// Spring的反射工具解析并遍历class里的方法ReflectionUtils.doWithLocalMethods(targetClass, method - {// 如果方法含有注解PostConstruct就添加到currInitMethodsif (this.initAnnotationType ! null method.isAnnotationPresent(this.initAnnotationType)) {LifecycleElement element new LifecycleElement(method);currInitMethods.add(element);if (logger.isTraceEnabled()) {logger.trace(Found init method on class [ clazz.getName() ]: method);}}// 如果方法含有注解PreDestroy就添加到currDestroyMethodsif (this.destroyAnnotationType ! null method.isAnnotationPresent(this.destroyAnnotationType)) {currDestroyMethods.add(new LifecycleElement(method));if (logger.isTraceEnabled()) {logger.trace(Found destroy method on class [ clazz.getName() ]: method);}}});initMethods.addAll(0, currInitMethods);destroyMethods.addAll(currDestroyMethods);// *** 注意 ***// 这里它获取了父类所以这里只要父类有注解也会被加进来targetClass targetClass.getSuperclass();}while (targetClass ! null targetClass ! Object.class);// 都没有注解就返回空对象可以在一开始就已经过滤过了所以这里都是有的return (initMethods.isEmpty() destroyMethods.isEmpty() ? this.emptyLifecycleMetadata :new LifecycleMetadata(clazz, initMethods, destroyMethods));}注意 上面它是一个向上的循环先顺序遍历当前类再找父类一直往上找而有一点不一样
initMethods.addAll(0, currInitMethods);
destroyMethods.addAll(currDestroyMethods);initMethod每次都放第一个所以父类的PostConstruct应该在子类之前执行这个也和类的实例是一样的顺序。
destroyMethod是顺序放入执行顺序应该是子类先执行
这个执行顺序这里只是根据它放入的顺序的一个猜测最终顺序还得看它执行的过程。
执行
在初始化bean时spring执行了一个后置处理器而执行PostConstruct/PreDestroy就是在里面执行的 位置org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#postProcessBeforeInitialization LifecycleMetadata是在解析时生成的对象看一下它的属性 它的执行如下是通过遍历的方式所以PonstConstruct执行的顺序是先执行父类的然后执行子类的。 至于PreDestroy它是在Spring容器关闭时执行的有ApplicationContext触发位置
org.springframework.context.support.AbstractApplicationContext#doClose
同样最终执行的位置和PostConstruct是同一个处理器两个的代码几乎是一样的都是直接遍历解析号的方法集然后顺序执行不过这里先执行的是子类然后才是父类的。