查看原文
其他

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

ImportNew 2023-01-22

(给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; @Componentpublic 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 = 王炸!!

2、为什么建议你替换掉 SpringBoot 框架中的 Tomcat ?

3、Spring5 里边的新玩法!这种 URL 请求让我涨见识了!


看完本文有收获?请转发分享给更多人

关注「ImportNew」,提升Java技能

点赞和在看就是最大的支持❤️


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存