最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

 更新時(shí)間:2019年05月05日 14:47:08   作者:楊斌  
這篇文章主要介紹了詳解如何在低版本的Spring中快速實(shí)現(xiàn)類似自動(dòng)配置的功能,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧

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

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

需求和問(wèn)題

核心的訴求

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

比如說(shuō):

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

面臨的問(wèn)題

  • 3.x 的 Spring 沒(méi)有條件注解

因?yàn)闆](méi)有條件注解,所以我們不清楚在什么時(shí)候 需要/不需要 配置這些東西

  • 無(wú)法自動(dòng)定位需要加載的自動(dòng)配置

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

核心解決思路

條件判斷

  • 通過(guò) BeanFactoryPostProcessor 進(jìn)行判斷

Spring 為我們提供了一個(gè)擴(kuò)展點(diǎn),我們可以通過(guò) BeanFactoryPostProcessor 來(lái)解決條件判斷的問(wèn)題,它可以讓我們?cè)?BeanFactory 定義完之后、Bean 的初始化之前對(duì)我們這些 Bean 的定義做一些后置的處理??梢栽谶@個(gè)時(shí)候?qū)ξ覀兊?Bean 定義做判斷,看看當(dāng)前 存在/缺少 哪些 Bean 的定義,還可以增加一些 Bean 的定義 —— 加入一些自己定制的 Bean。

配置加載

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

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

Spring 提供的兩個(gè)擴(kuò)展點(diǎn)

BeanPostProcessor

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

BeanFactoryPostProcessor

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

關(guān)于 Bean 的一些定制

既然上面提到了 Spring 的兩個(gè)擴(kuò)展點(diǎn),這里就延展一下關(guān)于 Bean 的一些定制的方式。

Lifecycle Callback

InitializingBean / @PostConstruct / init-method

這部分是關(guān)于初始化的,可以在 Bean 的初始化之后做一些定制,這里有三種方式:

  • 實(shí)現(xiàn) InitializingBean 接口
  • 使用 @PostConstruct 注解
  • 在 Bean 定義的 XML 文件里給它指定一個(gè) init-method;亦或者在使用 @Bean 注解時(shí)指定 init-method

這些都可以讓我們這個(gè) Bean 在創(chuàng)建之后去調(diào)用特定的方法。

DisposableBean / @PreDestroy / destroy-method

這部分是在 Bean 回收的時(shí)候,我們?cè)撟龅囊恍┎僮???梢灾付ㄟ@個(gè) Bean 在銷毀的時(shí)候,如果:

  • 它實(shí)現(xiàn)了 DisposableBean 這個(gè)接口,那么 Spring 會(huì)去調(diào)用它相應(yīng)的方法
  • 也可以將 @PreDestroy 注解加在某個(gè)方法上,那么會(huì)在銷毀時(shí)調(diào)用這個(gè)方法
  • 在 Bean 定義的 XML 文件里給它指定一個(gè) destroy-method;亦或者在使用 @Bean 注解時(shí)指定 destroy-method,那么會(huì)在銷毀時(shí)調(diào)用這個(gè)方法

XxxAware 接口

  • ApplicationContextAware

可以把整個(gè) ApplicationContext 通過(guò)接口進(jìn)行注入,在這個(gè) Bean 里我們就可以獲得一個(gè)完整的 ApplicationContext。

  • BeanFactoryAware

與 ApplicationContextAware 類似。

  • BeanNameAware

可以把 Bean 的名字注入到這個(gè)實(shí)例中來(lái)。

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

一些常用操作

判斷類是否存在

  • ClassUitls.isPresent()

調(diào)用 Spring 提供的 ClassUitls.isPresent() 來(lái)判斷一個(gè)類是否存在當(dāng)前 Class Path 下。

判斷 Bean 是否已定義

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

注冊(cè) Bean 定義

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

擼起袖子加油干

理論就科普完了,下面就開始實(shí)踐。

在當(dāng)前的例子中,我們假定一下當(dāng)前環(huán)境為:沒(méi)有使用 Spring Boot 以及高版本的 Spring。

Step 1:模擬低版本的 Spring 環(huán)境

這里只是簡(jiǎn)單地引入了 spring-context 依賴,并沒(méi)有真正的使用 Spring 3.x 的版本,但也沒(méi)有使用 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:以實(shí)現(xiàn) BeanFactoryPostProcessor 接口為例

@Slf4j
public class GreetingBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

 @Override
 public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
  throws BeansException {

  // 判斷當(dāng)前 Class Path 下是否存在所需要的 GreetingApplicationRunner 這么一個(gè)類
  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());
  }
 }
}

注冊(cè)我們的 Bean(見 CustomStarterAutoConfiguration),如下有幾點(diǎn)是需要注意的:

  • 這里的方法定義為 static
  • 使用時(shí),如果兩項(xiàng)目不是在同個(gè)包下,需要主動(dòng)將當(dāng)前類加入到項(xiàng)目的 component-scan 里
@Configuration
public class CustomStarterAutoConfiguration {

 @Bean
 public static GreetingBeanFactoryPostProcessor greetingBeanFactoryPostProcessor() {
  return new GreetingBeanFactoryPostProcessor();
 }
}

Step 3:驗(yàn)證該自動(dòng)配置是否生效

在其他項(xiàng)目中添加依賴:

<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>

啟動(dòng)項(xiàng)目并觀察日志(見 custom-starter-examples),驗(yàn)證自動(dòng)配置是否生效了:

 .  ____     _      __ _ _
 /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/ ___)| |_)| | | | | || (_| | ) ) ) )
 ' |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: 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 中實(shí)現(xiàn)了類似自動(dòng)配置的功能。clap

代碼托管于 GitHub,歡迎 Star

參考鏈接

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

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java實(shí)現(xiàn)簡(jiǎn)易俄羅斯方塊

    Java實(shí)現(xiàn)簡(jiǎn)易俄羅斯方塊

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡(jiǎn)易俄羅斯方塊,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-06-06
  • 200行java代碼實(shí)現(xiàn)2048小游戲

    200行java代碼實(shí)現(xiàn)2048小游戲

    這篇文章主要為大家詳細(xì)介紹了200行java代碼實(shí)現(xiàn)2048小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-04-04
  • 深入解析Java編程中的boolean對(duì)象的運(yùn)用

    深入解析Java編程中的boolean對(duì)象的運(yùn)用

    這篇文章主要介紹了Java編程中的boolean對(duì)象的運(yùn)用,是Java入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下
    2015-10-10
  • Java自帶注解、元注解、自定義注解的區(qū)別

    Java自帶注解、元注解、自定義注解的區(qū)別

    本文主要介紹了Java自帶注解、元注解、自定義注解的區(qū)別,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06
  • java8中NIO緩沖區(qū)(Buffer)的數(shù)據(jù)存儲(chǔ)詳解

    java8中NIO緩沖區(qū)(Buffer)的數(shù)據(jù)存儲(chǔ)詳解

    在本篇文章中小編給大家分享了關(guān)于java8中NIO緩沖區(qū)(Buffer)的數(shù)據(jù)存儲(chǔ)的相關(guān)知識(shí)點(diǎn),需要的朋友們參考下。
    2019-04-04
  • Spring?Boot整合Log4j2.xml的問(wèn)題及解決方法

    Spring?Boot整合Log4j2.xml的問(wèn)題及解決方法

    這篇文章主要介紹了Spring?Boot整合Log4j2.xml的問(wèn)題,本文給大家分享解決方案,需要的朋友可以參考下
    2023-09-09
  • Spring的AOP極簡(jiǎn)入門

    Spring的AOP極簡(jiǎn)入門

    今天小編就為大家分享一篇關(guān)于Spring的AOP極簡(jiǎn)入門,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-01-01
  • Spring JPA整合QueryDSL的示例代碼

    Spring JPA整合QueryDSL的示例代碼

    這篇文章主要介紹了Spring JPA整合QueryDSL的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-10-10
  • 學(xué)習(xí)Java之如何正確地向上轉(zhuǎn)型與向下轉(zhuǎn)型

    學(xué)習(xí)Java之如何正確地向上轉(zhuǎn)型與向下轉(zhuǎn)型

    面向?qū)ο蟮牡谌齻€(gè)特征是多態(tài),實(shí)現(xiàn)多態(tài)有三個(gè)必要條件:繼承、方法重寫和向上轉(zhuǎn)型,在學(xué)習(xí)多態(tài)之前,我們還要先學(xué)習(xí)Java的類型轉(zhuǎn)換,本篇文章就來(lái)帶大家認(rèn)識(shí)什么是類型轉(zhuǎn)換,看看類型轉(zhuǎn)換都有哪幾種情況,以及如何避免類型轉(zhuǎn)換時(shí)出現(xiàn)異常
    2023-05-05
  • Spring Kafka中@KafkaListener注解的參數(shù)與使用小結(jié)

    Spring Kafka中@KafkaListener注解的參數(shù)與使用小結(jié)

    @KafkaListener注解為開發(fā)者提供了一種聲明式的方式來(lái)定義消息監(jiān)聽器,本文主要介紹了Spring Kafka中@KafkaListener注解的參數(shù)與使用小結(jié),具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-06-06

最新評(píng)論

黎城县| 桐柏县| 云阳县| 郓城县| 社旗县| 宁南县| 齐河县| 安西县| 屯昌县| 龙泉市| 天等县| 运城市| 湘乡市| 贵州省| 普定县| 绿春县| 农安县| 桐柏县| 明溪县| 木里| 金塔县| 河东区| 天镇县| 宁夏| 冕宁县| 甘洛县| 图木舒克市| 寿宁县| 陈巴尔虎旗| 行唐县| 宁河县| 揭西县| 伊金霍洛旗| 康乐县| 宿松县| 普定县| 临朐县| 阿瓦提县| 镇安县| 金湖县| 福贡县|