如何在不修改原有类的情况下,对 @PostConstruct 的方法做 try...catch
(给ImportNew加星标,提高Java技能)
事情是酱紫的
项目就是个 Spring Boot 工程。
然后里面有一个类,有个方法加了 @PostConstruct。但是由于某些外部原因,这个方法会抛异常,因此本地服务起不起来,也没办法进行功能测试(比如这个 RemoteServer.getConfig() 抛异常)。
最直接的办法就是,在这个初始化方法里面加个 try...catch,测试,改 bug,去掉try...catch,提交,push……
But,一般这种情况,如果是改代码,难免会遇到一次忘了还原的情况,然后就直接提交,然后就发生线上事故了。
所以就有了题目里面的问题:不修改原始类的情况下,给 init 方法加个 try...catch。
先来结论
Step 1:在项目中加个包 gitignored
Step 2:右键这个包,git->add to gitignore -> .gitignore。如此一来,这个包下面所有的类,都不会进入git,从而只会存在于本地(换句话说就是可以随便整,不用担心误伤项目代码)。
Step3:最后一步,在上一步建的包下面,新建一个类:
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor;
import org.springframework.stereotype.Component;
@Component
public class TryCatchInitMethods extends CommonAnnotationBeanPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
registry.getBeanDefinition(AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME).setBeanClassName(getClass().getName());
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (!"xxxModule".equals(beanName)) {
return super.postProcessBeforeInitialization(bean, beanName);
}
try {
return super.postProcessBeforeInitialization(bean, beanName);
} catch (Exception e) {
e.printStackTrace();
return bean;
}
}
}
走近科学
接下来是解密时刻,这其实是利用 Spring 生命周期的一个扩展。为了方便不了解 Spring 的同学理解,这里我画一个简略图,只包含相关的重点。
Spring 容器启动,其实是一个流程化的工作,也就是步骤 1、步骤 2、步骤 3……看似神秘,但多研究研究,熟悉之后就会豁然开朗,然后感叹大神就是牛!
Spring 简略流程如下:
一些相关解释
1. BeanDefinition
其实就是spring-bean的“图纸”,用来描述这个 bean 是什么类型,有哪些初始化方法等等(信息很多,想了解可以直接看org.springframework.beans.factory.config.BeanDefinition)。
2. 切入点 BeanDefinitionRegistryPostProcessor
这是个 Spring 自己提供的接口,实现它就可以人为地对已存在的 BeanDefinition 做一些操作。
3. 初始化 CommonAnnotationBeanPostProcessor
这是 Spring 内置的一个处理器,会注册成 SpringBean(这个 bean 的名字叫做AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)。
每个bean在构建完成后,都会交给一系列 PostProcessor 去执行一些流程化操作,其中 CommonAnnotationBeanPostProcessor.postProcessBeforeInitialization 这个方法就是负责调用 @PostConstruct。
关键时刻
我加的这个类 TryCatchInitMethods,实现了BeanDefinitionRegistryPostProcessor,继承了CommonAnnotationBeanPostProcessor。也就是说,它具备了修改 BeanDefinition 和执行 bean 初始化方法的能力。
1. postProcessBeanDefinitionRegistry 方法
负责修改 BeanDefinition,在这里我把 AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME 这个 bean 的类型,设置成了 TryCatchInitMethods。也就是用我的类,直接替换了 Spring 内置的那个 CommonAnnotationBeanPostProcessor。
2. 既然已经替换了 CommonAnnotationBeanPostProcessor,那么在执行初始化方法的时候,实际上是调用的 TryCatchInitMethods.postProcessBeforeInitialization 方法,在这里判断,如果 Bean 是 XxxModule,就加一个 try...catch。
结尾
看起来,写了个花里胡哨的代码,还搞了一大串解释,好像还没有直接在原始类里面写 try...catch 来得有效。
有点繁琐是真,但是好处就是,完全不用担心粗心导致项目代码被破坏(忘了还原代码)。
还有一个好处就是,这个例子需要对 Spring 生命周期有一定程度的理解,所以这也是强化知识理解的一种途径(在思考解决方案的过程中,我也回顾了一下 Spring 的源码,才找到办法。这种状态非常好,因为学习源码最有效的途径就是,遇到问题,然后去源码中寻找答案)。
转自:肥肥技术宅
链接:blog.csdn.net/m0_71777195/article/details/128683451
- EOF -
1、Spring Boot 联合 Disruptor = 王炸!!
看完本文有收获?请转发分享给更多人
关注「ImportNew」,提升Java技能
点赞和在看就是最大的支持❤️