如何在不修改原有类的情况下,对@PostConstruct的方法做try...catch

3552 篇文章 107 订阅

事情是酱紫的

项目就是个SpringBoot工程

然后里面有一个类,有个方法加了@PostConstruct,但是由于某些外部原因,这个方法会抛异常,因此本地服务起不起来,也没办法进行功能测试(比如这个RemoteServer.getConfig()抛异常)。

最直接的办法就是,在这个初始化方法里面加个try...catch,测试,改bug,去掉try...catch,提交,push...

But,一般这种情况,如果是改代码,难免会遇到一次忘了还原的情况,然后就直接提交,然后就线上事故了

所以就有了题目里面的问题:不修改原始类的情况下,给init方法加个try...catch

先来结论

  1. Step1:在项目中加个包 gitignored
  2. step2:右键这个包,git->add to gitignore -> .gitignore。如此一来,这个包下面所有的类,都不会进入git,从而只会存在于本地(换句话说就是可以随便整,不用担心误伤项目代码)

  3. 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的源码,才找到办法,这种状态非常好,因为学习源码最有效的途径就是,遇到问题,然后去源码中寻找答案)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值