您好,登錄后才能下訂單哦!
在 Spring 4 后才引入了 @Conditional 等條件注解,它是 Spring Boot 中實現自動配置的最大功臣!
那么問題來了:如果我們還在使用 Spring 3.x 的老版本,這時候要怎么實現一個自動配置呢?
需求和問題
核心的訴求
比如說:
面臨的問題
因為沒有條件注解,所以我們不清楚在什么時候 需要/不需要 配置這些東西
此時我們沒有辦法像 Spring Boot 的自動配置那樣讓框架自動加載我們的配置,我們要使用一些別的手段讓 Spring 可以加載到我們定制的這些功能。
核心解決思路
條件判斷
Spring 為我們提供了一個擴展點,我們可以通過 BeanFactoryPostProcessor 來解決條件判斷的問題,它可以讓我們在 BeanFactory 定義完之后、Bean 的初始化之前對我們這些 Bean 的定義做一些后置的處理。可以在這個時候對我們的 Bean 定義做判斷,看看當前 存在/缺少 哪些 Bean 的定義,還可以增加一些 Bean 的定義 —— 加入一些自己定制的 Bean。
配置加載
可以考慮編寫自己的 Java Config 類,并把它加到 component-scan 里面,然后想辦法讓現在系統的 component-scan 包含我們編寫的 Java Config 類;也可以編寫 XML 文件,如果當前系統使用 XML 的方式,那么它加載的路徑上是否可以加載我們的 XML 文件,如果不行就可以使用手動 import 這個文件。
Spring 提供的兩個擴展點
BeanPostProcessor
BeanFactoryPostProcessor
關于 Bean 的一些定制
既然上面提到了 Spring 的兩個擴展點,這里就延展一下關于 Bean 的一些定制的方式。
Lifecycle Callback
InitializingBean / @PostConstruct / init-method
這部分是關于初始化的,可以在 Bean 的初始化之后做一些定制,這里有三種方式:
這些都可以讓我們這個 Bean 在創建之后去調用特定的方法。
DisposableBean / @PreDestroy / destroy-method
這部分是在 Bean 回收的時候,我們該做的一些操作。可以指定這個 Bean 在銷毀的時候,如果:
XxxAware 接口
可以把整個 ApplicationContext 通過接口進行注入,在這個 Bean 里我們就可以獲得一個完整的 ApplicationContext。
與 ApplicationContextAware 類似。
可以把 Bean 的名字注入到這個實例中來。
如果對源碼感興趣,可見:org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean\
如果當前 Bean 存在 close 或 shutdown 方法名的方法時,會被 Spring 視為 destroy-method,在銷毀時會進行調用。
一些常用操作
判斷類是否存在
調用 Spring 提供的 ClassUitls.isPresent() 來判斷一個類是否存在當前 Class Path 下。
判斷 Bean 是否已定義
注冊 Bean 定義
擼起袖子加油干
理論就科普完了,下面就開始實踐。
在當前的例子中,我們假定一下當前環境為:沒有使用 Spring Boot 以及高版本的 Spring。
Step 1:模擬低版本的 Spring 環境
這里只是簡單地引入了 spring-context 依賴,并沒有真正的使用 Spring 3.x 的版本,但也沒有使用 Spring 4 以上的一些特性。
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>io.github.y0ngb1n.samples</groupId> <artifactId>custom-starter-core</artifactId> <scope>provided</scope> </dependency> </dependencies>
Step 2:以實現 BeanFactoryPostProcessor 接口為例
@Slf4j public class GreetingBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { // 判斷當前 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) { // 當前上下文已存在 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),如下有幾點是需要注意的:
@Configuration public class CustomStarterAutoConfiguration { @Bean public static GreetingBeanFactoryPostProcessor greetingBeanFactoryPostProcessor() { return new GreetingBeanFactoryPostProcessor(); } }
Step 3:驗證該自動配置是否生效
在其他項目中添加依賴:
<dependencies> ... <dependency> <groupId>io.github.y0ngb1n.samples</groupId> <artifactId>custom-starter-spring-lt4-autoconfigure</artifactId> </dependency> <dependency> <groupId>io.github.y0ngb1n.samples</groupId> <artifactId>custom-starter-core</artifactId> </dependency> ... </dependencies>
啟動項目并觀察日志(見 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 中實現了類似自動配置的功能。clap
代碼托管于 GitHub,歡迎 Star
參考鏈接
https://github.com/y0ngb1n/spring-boot-samples
https://github.com/digitalsonic/geektime-spring-family
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。