真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

詳解如何在低版本的Spring中快速實現(xiàn)類似自動配置的功能

在 Spring 4 后才引入了 @Conditional 等條件注解,它是 Spring Boot 中實現(xiàn)自動配置的最大功臣!

十年的五臺網(wǎng)站建設經(jīng)驗,針對設計、前端、開發(fā)、售后、文案、推廣等六對一服務,響應快,48小時及時工作處理。營銷型網(wǎng)站的優(yōu)勢是能夠根據(jù)用戶設備顯示端的尺寸不同,自動調(diào)整五臺建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設計,從而大程度地提升瀏覽體驗。創(chuàng)新互聯(lián)建站從事“五臺網(wǎng)站設計”,“五臺網(wǎng)站推廣”以來,每個客戶項目都認真落實執(zhí)行。

那么問題來了:如果我們還在使用 Spring 3.x 的老版本,這時候要怎么實現(xiàn)一個自動配置呢?

需求和問題

核心的訴求

  1. 現(xiàn)存系統(tǒng),不打算重構(gòu)
  2. Spring 版本為 3.x,也不打算升級版本和引入 Spring Boot
  3. 期望能夠在少改代碼的前提下實現(xiàn)功能增強

比如說:

  1. 希望能夠給全站統(tǒng)一添加上日志記錄(如:RPC 框架 Web 調(diào)用的摘要信息、數(shù)據(jù)庫訪問層的摘要信息),這個其實是個通用的功能。
  2. 我們引用了一些基礎設施,并想對這些基礎設施的功能作進一步的增強,這時候就應該從框架的層面來解決這個問題。

面臨的問題

  • 3.x 的 Spring 沒有條件注解

因為沒有條件注解,所以我們不清楚在什么時候 需要/不需要 配置這些東西

  • 無法自動定位需要加載的自動配置

此時我們沒有辦法像 Spring Boot 的自動配置那樣讓框架自動加載我們的配置,我們要使用一些別的手段讓 Spring 可以加載到我們定制的這些功能。

核心解決思路

條件判斷

  • 通過 BeanFactoryPostProcessor 進行判斷

Spring 為我們提供了一個擴展點,我們可以通過 BeanFactoryPostProcessor 來解決條件判斷的問題,它可以讓我們在 BeanFactory 定義完之后、Bean 的初始化之前對我們這些 Bean 的定義做一些后置的處理??梢栽谶@個時候?qū)ξ覀兊?Bean 定義做判斷,看看當前 存在/缺少 哪些 Bean 的定義,還可以增加一些 Bean 的定義 —— 加入一些自己定制的 Bean。

配置加載

  • 編寫 Java Config 類
  • 引入配置類
    • 通過 component-scan
    • 通過 XML 文件 import

可以考慮編寫自己的 Java Config 類,并把它加到 component-scan 里面,然后想辦法讓現(xiàn)在系統(tǒng)的 component-scan 包含我們編寫的 Java Config 類;也可以編寫 XML 文件,如果當前系統(tǒng)使用 XML 的方式,那么它加載的路徑上是否可以加載我們的 XML 文件,如果不行就可以使用手動 import 這個文件。

Spring 提供的兩個擴展點

BeanPostProcessor

  • 針對 Bean 實例
  • 在 Bean 創(chuàng)建后提供定制邏輯回調(diào)

BeanFactoryPostProcessor

  • 針對 Bean 定義
  • 在容器創(chuàng)建 Bean 前獲取配置元數(shù)據(jù)
  • Java Config 中需要定義為 static 方法(如果不定義,Spring 在啟動時會報一個 warning,你可嘗試一下)

關于 Bean 的一些定制

既然上面提到了 Spring 的兩個擴展點,這里就延展一下關于 Bean 的一些定制的方式。

Lifecycle Callback

InitializingBean / @PostConstruct / init-method

這部分是關于初始化的,可以在 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)用它相應的方法
  • 也可以將 @PreDestroy 注解加在某個方法上,那么會在銷毀時調(diào)用這個方法
  • 在 Bean 定義的 XML 文件里給它指定一個 destroy-method;亦或者在使用 @Bean 注解時指定 destroy-method,那么會在銷毀時調(diào)用這個方法

XxxAware 接口

  • ApplicationContextAware

可以把整個 ApplicationContext 通過接口進行注入,在這個 Bean 里我們就可以獲得一個完整的 ApplicationContext。

  • BeanFactoryAware

與 ApplicationContextAware 類似。

  • BeanNameAware

可以把 Bean 的名字注入到這個實例中來。

如果對源碼感興趣,可見:org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean\
如果當前 Bean 存在 close 或 shutdown 方法名的方法時,會被 Spring 視為 destroy-method,在銷毀時會進行調(diào)用。

一些常用操作

判斷類是否存在

  • ClassUitls.isPresent()

調(diào)用 Spring 提供的 ClassUitls.isPresent() 來判斷一個類是否存在當前 Class Path 下。

判斷 Bean 是否已定義

  • ListableBeanFactory.containsBeanDefinition():判斷 Bean 是否已定義。
  • ListableBeanFactory.getBeanNamesForType():可以查看某些類型的 Bean 都有哪些名字已經(jīng)被定義了。

注冊 Bean 定義

  • BeanDefinitionRegistry.registerBeanDefinition()
    • GenericBeanDefinition
  • BeanFactory.registerSingleton()

擼起袖子加油干

理論就科普完了,下面就開始實踐。

在當前的例子中,我們假定一下當前環(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 {

  // 判斷當前 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),如下有幾點是需要注意的:

  • 這里的方法定義為 static
  • 使用時,如果兩項目不是在同個包下,需要主動將當前類加入到項目的 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)了類似自動配置的功能。clap

代碼托管于 GitHub,歡迎 Star

參考鏈接

https://github.com/y0ngb1n/spring-boot-samples
https://github.com/digitalsonic/geektime-spring-family

以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。


當前文章:詳解如何在低版本的Spring中快速實現(xiàn)類似自動配置的功能
鏈接分享:http://weahome.cn/article/pgehpd.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部