Spring如何重寫內(nèi)置Bean(Controller、Service等)
Spring重寫內(nèi)置Bean(Controller、Service等)
場景
svc1.jar
中有一個TestController
類,當(dāng)前源代碼工程 demo 依賴了這個svc1.jar
。- 當(dāng)前 demo 工程啟動正常,TestController 被初始化,
TestController
中提供的接口/svc1/test1
、/svc1/test2
、/svc1/test3
均可以正常訪問。 - 因接口
/svc1/test2
對應(yīng)的方法中邏輯處理不滿足需求,需要對這個方法進(jìn)行重寫處理。 - 該接口在
svc1.jar
包,不能直接修改源代碼。
處理方式一(簡單處理)
主要思路是在 Spring 注冊 Bean 之后進(jìn)行 PostProcessor 時,將已經(jīng)注冊的 svc1.jar
中的 TestController 從上下文中刪除。
使我們新創(chuàng)建的繼承 TestController 的重寫類能被正常加載實(shí)例化,而不出現(xiàn) PathMapping 重復(fù)的報錯沖突。
主要代碼如下:
/** * 排除容器中的Bean * * @author 單紅宇 * @since 2024/11/28 13:14 */ @Slf4j @Configuration public class ExcludeComponentConfiguration implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { registry.removeBeanDefinition(getDefaultBeanName(FileController.class)); registry.removeBeanDefinition(getDefaultBeanName(UserController.class)); } /** * 根據(jù)類 class 獲取 spring 默認(rèn)的 beanName * * @param clazz clazz * @return String */ private String getDefaultBeanName(Class<?> clazz) { // 測試內(nèi)部類 AutowireUtils.ObjectFactoryDelegatingInvocationHandler 的默認(rèn) BeanName // StringUtils.uncapitalizeAsProperty(ClassUtils.getShortName(AutowireUtils.ObjectFactoryDelegatingInvocationHandler.class.getName())) // spring 6.0 之前使用 StringUtils.uncapitalize() return org.springframework.util.StringUtils.uncapitalizeAsProperty(ClassUtils.getShortName(clazz.getName())); } }
/** * 重寫FileController類 * * @author 單紅宇 * @since 2024/11/28 12:03 */ @RestController //該注解必須要有,Mapping那些注解不需要 public class OverrideFileController extends FileController { @Override public void fileDownload(@RequestParam("fileId") Long fileId, @RequestParam("isInline") Integer isInline, HttpServletResponse response, HttpServletRequest request) { System.out.println("override filedownload..."); } }
處理方式二(自定義注解)
我們可以自定義一個注解 @ExcludeBean
,然后使用該注解來很方便的在任何地方來排除需要排除的類。
1、自定義注解
/** * 自定義注解,用來排除原本會在spring上下文中的bean。 * 一般用于刪除一個不可以修改的Bean,然后自定義一個類繼承原有類然后重寫特定的方法取代原來的類 * * @author 單紅宇 * @since 2024/12/5 9:36 */ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface ExcludeBean { Class<?>[] clazz() default {}; String[] name() default {}; }
2、自動配置類
/** * 排除 Spring Bean 自動配置類 * @author 單紅宇 * @since 2024/12/5 9:13 */ @AutoConfiguration public class ExcludeBeanAutoConfiguration implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { // not implemented } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { if (beanFactory instanceof BeanDefinitionRegistry bdr) { Arrays.stream(beanFactory.getBeanNamesForAnnotation(ExcludeBean.class)) .map(item -> beanFactory.findAnnotationOnBean(item, ExcludeBean.class, false)).filter(Objects::nonNull) .flatMap(item -> Stream.concat(Arrays.stream(item.name()), Arrays.stream(item.clazz()).map(cls -> org.springframework.util.StringUtils.uncapitalizeAsProperty(ClassUtils.getShortName(cls.getName()))))) .distinct() .forEach(bdr::removeBeanDefinition); } } }
3、配置自動配置
@AutoConfiguration
自動配置注解啟用的方法詳見文章: SpringBoot 自動配置 @AutoConfiguration 在 2.7 版本新增
4、注解使用示例
我們來重寫一個 Controller 的方法,主要應(yīng)用場景是:目標(biāo) Controller 是通過 jar 包依賴過來的,并且我們需要重寫其中的一個方法邏輯。
/** * 重寫 Controller 類 TestApi 中的特定方法 * * @author 單紅宇 * @since 2024/11/28 9:06 */ @ExcludeBean(clazz = TestApi.class) @RestController public class OverrideTestApi extends TestApi { /** * 重寫 Hello 方法 * * @return String */ @Override public String hello() { return "Hello, Override!"; } }
補(bǔ)充
如果是基于一個接口然后有多個實(shí)現(xiàn)類的多實(shí)例場景,使用的地方是基于接口注入的。
也可以使用 @Primary
注解來修飾特定的類。
但是對于如 Controller
以及其他沒有定義并通過接口注入方式的可能就要使用我們本文的方法了。
總結(jié)
以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot定時任務(wù)兩種(Spring Schedule 與 Quartz 整合 )實(shí)現(xiàn)方法
本篇文章主要介紹了SpringBoot定時任務(wù)兩種(Spring Schedule 與 Quartz 整合 )實(shí)現(xiàn)方法,詳細(xì)的介紹了Spring Schedule 與 Quartz 整合的兩種方法,有興趣的可以了解一下。2017-03-03用java將GBK工程轉(zhuǎn)為uft8的方法實(shí)例
本篇文章主要介紹了用java將GBK工程轉(zhuǎn)為uft8的方法實(shí)例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-08-08SpringCloud使用AOP統(tǒng)一處理Web請求日志實(shí)現(xiàn)步驟
這篇文章主要為大家介紹了SpringCloud使用AOP統(tǒng)一處理Web請求日志實(shí)現(xiàn)步驟,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08SpringBoot 項(xiàng)目中的圖片處理策略之本地存儲與路徑映射
在SpringBoot項(xiàng)目中,靜態(tài)資源存放在static目錄下,使得前端可以通過URL來訪問這些資源,我們就需要將文件系統(tǒng)的文件路徑與URL建立一個映射關(guān)系,把文件系統(tǒng)中的文件當(dāng)成我們的靜態(tài)資源即可,本文給大家介紹SpringBoot本地存儲與路徑映射的相關(guān)知識,感興趣的朋友一起看看吧2023-12-12Spring容器擴(kuò)展機(jī)制的實(shí)現(xiàn)原理
這篇文章主要介紹了Spring容器擴(kuò)展機(jī)制的實(shí)現(xiàn)原理,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-11-11SSH框架網(wǎng)上商城項(xiàng)目第24戰(zhàn)之Struts2中處理多個Model請求的方法
這篇文章主要為大家詳細(xì)介紹了SSH框架網(wǎng)上商城項(xiàng)目第24戰(zhàn)之Struts2中處理多個Model請求的方法,感興趣的小伙伴們可以參考一下2016-06-06一鍵清除maven倉庫中下載失敗的jar包的實(shí)現(xiàn)方法
這篇文章主要介紹了一鍵清除maven倉庫中下載失敗的jar包的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07IDEA報錯:Process terminated的問題及解決
這篇文章主要介紹了IDEA報錯:Process terminated的問題及解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-11-11