摘要:一些常用操作判斷類是否存在調(diào)用提供的來判斷一個類是否存在當(dāng)前下。在當(dāng)前的例子中,我們假定一下當(dāng)前環(huán)境為沒有使用以及高版本的。模擬低版本的環(huán)境這里只是簡單地引入了依賴,并沒有真正的使用的版本,但也沒有使用以上的一些特性。
感謝您的閱讀,本文由 楊斌的博客 版權(quán)所有。
如若轉(zhuǎn)載,請注明出處:楊斌的博客(https://y0ngb1n.github.io/a/c...)
在 Spring 4 后才引入了 @Conditional 等條件注解,它是 Spring Boot 中實現(xiàn)自動配置的最大功臣!
那么問題來了:如果我們還在使用 Spring 3.x 的老版本,這時候要怎么實現(xiàn)一個自動配置呢?
代碼托管于 GitHub,歡迎 Star
需求和問題 核心的訴求現(xiàn)存系統(tǒng),不打算重構(gòu)
Spring 版本為 3.x,也不打算升級版本和引入 Spring Boot
期望能夠在少改代碼的前提下實現(xiàn)功能增強(qiáng)
比如說:
希望能夠給全站統(tǒng)一添加上日志記錄(如:RPC 框架 Web 調(diào)用的摘要信息、數(shù)據(jù)庫訪問層的摘要信息),這個其實是個通用的功能。
我們引用了一些基礎(chǔ)設(shè)施,并想對這些基礎(chǔ)設(shè)施的功能作進(jìn)一步的增強(qiáng),這時候就應(yīng)該從框架的層面來解決這個問題。
面臨的問題3.x 的 Spring 沒有條件注解
因為沒有條件注解,所以我們不清楚在什么時候 需要/不需要 配置這些東西
無法自動定位需要加載的自動配置
此時我們沒有辦法像 Spring Boot 的自動配置那樣讓框架自動加載我們的配置,我們要使用一些別的手段讓 Spring 可以加載到我們定制的這些功能。
核心解決思路 條件判斷通過 BeanFactoryPostProcessor 進(jìn)行判斷
Spring 為我們提供了一個擴(kuò)展點,我們可以通過 BeanFactoryPostProcessor 來解決條件判斷的問題,它可以讓我們在 BeanFactory 定義完之后、Bean 的初始化之前對我們這些 Bean 的定義做一些后置的處理??梢栽谶@個時候?qū)ξ覀兊?Bean 定義做判斷,看看當(dāng)前 存在/缺少 哪些 Bean 的定義,還可以增加一些 Bean 的定義 —— 加入一些自己定制的 Bean。
配置加載編寫 Java Config 類
引入配置類
通過 component-scan
通過 XML 文件 import
可以考慮編寫自己的 Java Config 類,并把它加到 component-scan 里面,然后想辦法讓現(xiàn)在系統(tǒng)的 component-scan 包含我們編寫的 Java Config 類;也可以編寫 XML 文件,如果當(dāng)前系統(tǒng)使用 XML 的方式,那么它加載的路徑上是否可以加載我們的 XML 文件,如果不行就可以使用手動 import 這個文件。
Spring 提供的兩個擴(kuò)展點 BeanPostProcessor針對 Bean 實例
在 Bean 創(chuàng)建后提供定制邏輯回調(diào)
BeanFactoryPostProcessor針對 Bean 定義
在容器創(chuàng)建 Bean 前獲取配置元數(shù)據(jù)
Java Config 中需要定義為 static 方法(如果不定義,Spring 在啟動時會報一個 warning,你可嘗試一下)
關(guān)于 Bean 的一些定制既然上面提到了 Spring 的兩個擴(kuò)展點,這里就延展一下關(guān)于 Bean 的一些定制的方式。Lifecycle Callback
InitializingBean / @PostConstruct / init-method
這部分是關(guān)于初始化的,可以在 Bean 的初始化之后做一些定制,這里有三種方式:
實現(xiàn) InitializingBean 接口
使用 @PostConstruct 注解
在 Bean 定義的 XML 文件里給它指定一個 init-method;亦或者在使用 @Bean 注解時指定 init-method
這些都可以讓我們這個 Bean 在創(chuàng)建之后去調(diào)用特定的方法。
DisposableBean / @PreDestroy / destroy-method
這部分是在 Bean 回收的時候,我們該做的一些操作??梢灾付ㄟ@個 Bean 在銷毀的時候,如果:
它實現(xiàn)了 DisposableBean 這個接口,那么 Spring 會去調(diào)用它相應(yīng)的方法
也可以將 @PreDestroy 注解加在某個方法上,那么會在銷毀時調(diào)用這個方法
在 Bean 定義的 XML 文件里給它指定一個 destroy-method;亦或者在使用 @Bean 注解時指定 destroy-method,那么會在銷毀時調(diào)用這個方法
XxxAware 接口ApplicationContextAware
可以把整個 ApplicationContext 通過接口進(jìn)行注入,在這個 Bean 里我們就可以獲得一個完整的 ApplicationContext。
BeanFactoryAware
與 ApplicationContextAware 類似。
BeanNameAware
可以把 Bean 的名字注入到這個實例中來。
如果對源碼感興趣,可見:org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean一些常用操作 判斷類是否存在
如果當(dāng)前 Bean 存在 close 或 shutdown 方法名的方法時,會被 Spring 視為 destroy-method,在銷毀時會進(jìn)行調(diào)用。
ClassUitls.isPresent()
調(diào)用 Spring 提供的 ClassUitls.isPresent() 來判斷一個類是否存在當(dāng)前 Class Path 下。
判斷 Bean 是否已定義ListableBeanFactory.containsBeanDefinition():判斷 Bean 是否已定義。
ListableBeanFactory.getBeanNamesForType():可以查看某些類型的 Bean 都有哪些名字已經(jīng)被定義了。
注冊 Bean 定義
BeanDefinitionRegistry.registerBeanDefinition()
GenericBeanDefinition
BeanFactory.registerSingleton()
擼起袖子加油干理論就科普完了,下面就開始實踐。
在當(dāng)前的例子中,我們假定一下當(dāng)前環(huán)境為:沒有使用 Spring Boot 以及高版本的 Spring。
Step 1:模擬低版本的 Spring 環(huán)境
這里只是簡單地引入了 spring-context 依賴,并沒有真正的使用 Spring 3.x 的版本,但也沒有使用 Spring 4 以上的一些特性。
org.springframework spring-context org.projectlombok lombok io.github.y0ngb1n.samples custom-starter-core provided
Step 2:以實現(xiàn) BeanFactoryPostProcessor 接口為例
@Slf4j public class GreetingBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { // 判斷當(dāng)前 Class Path 下是否存在所需要的 GreetingApplicationRunner 這么一個類 boolean hasClass = ClassUtils .isPresent("io.github.y0ngb1n.samples.greeting.GreetingApplicationRunner", GreetingBeanFactoryPostProcessor.class.getClassLoader()); if (!hasClass) { // 類不存在 log.info("GreetingApplicationRunner is NOT present in CLASSPATH."); return; } // 是否存在 id 為 greetingApplicationRunner 的 Bean 定義 boolean hasDefinition = beanFactory.containsBeanDefinition("greetingApplicationRunner"); if (hasDefinition) { // 當(dāng)前上下文已存在 greetingApplicationRunner log.info("We already have a greetingApplicationRunner bean registered."); return; } register(beanFactory); } private void register(ConfigurableListableBeanFactory beanFactory) { if (beanFactory instanceof BeanDefinitionRegistry) { GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); beanDefinition.setBeanClass(GreetingApplicationRunner.class); ((BeanDefinitionRegistry) beanFactory) .registerBeanDefinition("greetingApplicationRunner", beanDefinition); } else { beanFactory.registerSingleton("greetingApplicationRunner", new GreetingApplicationRunner()); } } }
注冊我們的 Bean(見 CustomStarterAutoConfiguration),如下有幾點是需要注意的:
這里的方法定義為 static
使用時,如果兩項目不是在同個包下,需要主動將當(dāng)前類加入到項目的 component-scan 里
@Configuration public class CustomStarterAutoConfiguration { @Bean public static GreetingBeanFactoryPostProcessor greetingBeanFactoryPostProcessor() { return new GreetingBeanFactoryPostProcessor(); } }
Step 3:驗證該自動配置是否生效
在其他項目中添加依賴:
... io.github.y0ngb1n.samples custom-starter-spring-lt4-autoconfigure ... io.github.y0ngb1n.samples custom-starter-core
啟動項目并觀察日志(見 custom-starter-examples),驗證自動配置是否生效了:
. ____ _ __ _ _ / / ___"_ __ _ _(_)_ __ __ _ ( ( )\___ | "_ | "_| | "_ / _` | / ___)| |_)| | | | | || (_| | ) ) ) ) " |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.1.0.RELEASE) 2019-05-02 20:47:27.692 INFO 11460 --- [ main] i.g.y.s.d.AutoconfigureDemoApplication : Starting AutoconfigureDemoApplication on HP with PID 11460 ... 2019-05-02 20:47:27.704 INFO 11460 --- [ main] i.g.y.s.d.AutoconfigureDemoApplication : No active profile set, falling back to default profiles: default 2019-05-02 20:47:29.558 INFO 11460 --- [ main] i.g.y.s.g.GreetingApplicationRunner : Initializing GreetingApplicationRunner. 2019-05-02 20:47:29.577 INFO 11460 --- [ main] i.g.y.s.d.AutoconfigureDemoApplication : Started AutoconfigureDemoApplication in 3.951 seconds (JVM running for 14.351) 2019-05-02 20:47:29.578 INFO 11460 --- [ main] i.g.y.s.g.GreetingApplicationRunner : Hello everyone! We all like Spring!
到這里,已成功在低版本的 Spring 中實現(xiàn)了類似自動配置的功能。
參考鏈接https://github.com/y0ngb1n/sp...
https://github.com/digitalson...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/74380.html
摘要:小編一哥們和我吐槽自家的煩惱原本一個有錢有閑的證券行業(yè)經(jīng)理一年前被老板派去支持創(chuàng)新業(yè)務(wù)探索因為新型業(yè)務(wù)在不斷加速鋪開當(dāng)前的單體式應(yīng)用復(fù)雜度越來越高業(yè)務(wù)上線過程繁瑣流程冗長資源分配耗時較多更新頻率越來越低人員也越來越顯得捉襟見肘這哥們于是開始 小編一哥們和我吐槽自家的煩惱原本一個有錢有閑的證券行業(yè)IT經(jīng)理一年前被老板派去支持創(chuàng)新業(yè)務(wù)探索因為新型業(yè)務(wù)在不斷加速鋪開當(dāng)前的單體式應(yīng)用復(fù)雜度越來...
摘要:本項目將使用配合最簡單的邏輯來展示一個基于的微服務(wù)全棧快速開發(fā)實踐的。提供一系列大型項目常用的非功能性特征,比如內(nèi)嵌服務(wù)器,安全,指標(biāo),健康檢測,外部化配置。 SprintBoot-Vue SpringBoot + 前端MVVM 基于Java的微服務(wù)全??焖匍_發(fā)實踐 showImg(https://segmentfault.com/img/remote/1460000010167913...
摘要:本項目將使用配合最簡單的邏輯來展示一個基于的微服務(wù)全??焖匍_發(fā)實踐的。提供一系列大型項目常用的非功能性特征,比如內(nèi)嵌服務(wù)器,安全,指標(biāo),健康檢測,外部化配置。 SprintBoot-Vue SpringBoot + 前端MVVM 基于Java的微服務(wù)全棧快速開發(fā)實踐 showImg(https://segmentfault.com/img/remote/1460000010167913...
摘要:響應(yīng)式編程是基于異步和事件驅(qū)動的非阻塞程序,只是垂直通過在內(nèi)啟動少量線程擴(kuò)展,而不是水平通過集群擴(kuò)展。三特性常用的生產(chǎn)的特性如下響應(yīng)式編程模型適用性內(nèi)嵌容器組件還有對日志消息測試及擴(kuò)展等支持。 摘要: 原創(chuàng)出處 https://www.bysocket.com 「公眾號:泥瓦匠BYSocket 」歡迎關(guān)注和轉(zhuǎn)載,保留摘要,謝謝! 02:WebFlux 快速入門實踐 文章工程: JDK...
摘要:通過本教程的前兩篇基礎(chǔ)教程使用實現(xiàn)服務(wù)注冊與發(fā)現(xiàn)基礎(chǔ)教程支持的幾種服務(wù)消費方式我們已經(jīng)學(xué)會了,如何利用實現(xiàn)服務(wù)的注冊與發(fā)現(xiàn)。簡介除了實現(xiàn)了服務(wù)的注冊發(fā)現(xiàn)之外,還將配置中心功能整合在了一起。同時,值必須與上一階段中創(chuàng)建的配置匹配除了或者后綴。 通過本教程的前兩篇: 《Spring Cloud Alibaba基礎(chǔ)教程:使用Nacos實現(xiàn)服務(wù)注冊與發(fā)現(xiàn)》 《Spring Cloud Ali...
閱讀 1641·2021-09-22 15:25
閱讀 1519·2021-09-07 10:06
閱讀 3195·2019-08-30 15:53
閱讀 1099·2019-08-29 13:12
閱讀 3390·2019-08-29 13:07
閱讀 738·2019-08-28 18:19
閱讀 2278·2019-08-27 10:57
閱讀 995·2019-08-26 13:29