180531-Spring中JavaConfig知識小結

来源:https://www.cnblogs.com/yihuihui/archive/2018/06/02/9127388.html
-Advertisement-
Play Games

在Spring生態中,JavaConfig 如何優雅的替換 XML ...


原文鏈接:Spring中JavaConfig知識小結/

Sring中JavaConfig使用姿勢

去掉xml的配置方式,改成用Java來配置,最常見的就是將xml中的 bean定義, scanner包掃描,屬性文件的配置信息讀取等

I. 幾個基本註解

1. Configuration註解

在javaConfig中註解@Configuration用來代替一個xml文件,可以簡單的理解他們的作用是相等的,一般bean的定義也都是放在被這個註解修飾的類中

如一個基本的配置文件如下

@Configuration
@ComponentScan("com.git.hui.rabbit.spring")
public class SpringConfig {
    private Environment environment;

    @Autowired
    public void setEnvironment(Environment environment) {
        this.environment = environment;
        System.out.println("then env: " + environment);
    }

    @Bean(name="connectionFactory")
    public ConnectionFactory connectionFactory() {
        CachingConnectionFactory factory = new CachingConnectionFactory();
        factory.setHost("127.0.0.1");
        factory.setPort(5672);
        factory.setUsername("admin");
        factory.setPassword("admin");
        factory.setVirtualHost("/");
        return factory;
    }

    @Bean
    public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
        return new RabbitAdmin(connectionFactory);
    }
}

2. Bean 註解

上面的例子中,在方法上添加了@Bean註解,這個就相當於傳統的

<bean name="rabbitAdmin" class="org.springframework.amqp.rabbit.core.RabbitAdmin"/>

因此在需要引入rabbitAdmin實例的地方,可以如下使用

a. 屬性欄位上添加 @Autowired註解

public class RConsumer {
  @Autowired
  private RabbitAdmin rabbitAdmin;
}

b. 設置方法上添加 @Autowired註解

public class RConsumer {
  private RabbitAdmin rabbitAdmin;
  
  @Autowired
  public void setRabbitAdmin(RabbitAdmin rabbitAdmin) {
    this.rabbitAdmin = rabbitAdmin;
  }
}

c. 使用構造器的方式

public class RConsumer {
  private RabbitAdmin rabbitAdmin;
  public RConsumer(RabbitAdmin rabbitAdmin) {
        this.rabbitAdmin = rabbitAdmin;
  }
}

上面就是Spring容器支持的幾種典型的IoC方式

3. ComponentScan

這個類似於xml中的 <context:component-scan"/> 標簽

@ComponentScan("com.git.hui.rabbit.spring")
public class SpringConfig {
}

上面的這個配置,表示自動掃描包 com.git.hui.rabbit.spring 下麵的bean (要求類上添加了 @Component, @Repository, @Service)

那麼一個問題來了,如果一個類既被自動掃描載入,又顯示定義了bean,會怎樣?

package com.git.hui.rabbit.spring;

import org.springframework.stereotype.Component;
import java.util.concurrent.atomic.AtomicInteger;

@Component
public class TestBean {
    private static AtomicInteger count = new AtomicInteger(1);

    public TestBean() {
        System.out.println("testBean count: " + count.getAndAdd(1));
    }
}

對應的JavaConfig

@Configuration
@ComponentScan("com.git.hui.rabbit.spring")
public class SpringConfig {
    @Bean
    public TestBean testBean() {
        return new TestBean();
    }
}

實際測試,發現這個bean只會有一個實例,即輸出計數只會有一條,實際查看ApplicationContext中的內容,TestBean的實例,也確實只有一個,如果改成下麵這種場景呢

@Bean(name="testBean2")
public TestBean testBean() {
    return new TestBean();
}

會有兩條記錄輸出,實際查看容器中的Bean對象,會有兩個實例如下

  180531_JavaConfig01.jpg

這和我們的預期也是一樣的,因為一個類我可能需要多個不同的Bean實例來乾一些事情

那麼出現這種JavaConfig定義的beanName與自動掃描的衝突的情況會怎樣呢?

新增一個NewBean對象,

public class NewBean {
    private static AtomicInteger count = new AtomicInteger(1);

    public NewBean() {
        System.out.println(" newbean count: " + count.getAndAdd(1));
    }
}

在JavaConfig中新加一個bean定義,但是BeanName與自動掃描的TestBean重覆了

@Bean(name="testBean")
public NewBean newBean() {
  return new NewBean();
}

此時發現有意思的事情了,從Spring容器中,將查不到TestBean的實例,但是可以查到NewBean的實例

  180531_JavaConfig02.jpg

這個的表現是:

  • 當beanName出現衝突時,JavaConfig的優先順序會高於自動載入的,導致自動載入的Bean不會被載入到容器內

那麼跟著來的一個問題就是如果JavaConfig中定義了兩個相同的BeanName的bean呢?

@Bean(name = "testBean2")
public NewBean newBean() {
    return new NewBean();
}

@Bean(name = "testBean2")
public TestBean testBean() {
    return new TestBean();
}

因為我們TestBean上加了@Component註解,因此容器中至少有一個,但是否會有testBean2這個實例呢? 通過實際查看是沒有的,testBean2這個名被 NewBean 占領了

  180531_JavaConfig03.jpg

so,表現上看,加上實測,將上面的定義換個位置,得出下麵的結論

  • 當出現beanName重名時,先定義的Bean占優

然後就是最後一個問題了,當自動掃描時,兩個類包不同,但是類名相同,會怎樣?

package com.git.hui.rabbit.spring.demo;

import org.springframework.stereotype.Component;
import java.util.concurrent.atomic.AtomicInteger;

@Component
public class TestBean {
    private static AtomicInteger count = new AtomicInteger(1);

    public TestBean() {
        System.out.println(" demo.TestBean count: " + count.getAndAdd(1));
    }
}

實測,會拋出一個異常,在使用xml的配置方式時,經常見到的一個BeanName衝突的異常

org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'testBean' for bean class [com.git.hui.rabbit.spring.demo.TestBean] conflicts with existing, non-compatible bean definition of same name and class [com.git.hui.rabbit.spring.TestBean]

小結:

  • JavaConfig 定義的BeanName與自動掃描的BeanName衝突時,JavaConfig的定義的會被實例化
  • JavaConfig 中定義了BeanName相同的Bean時,優先定義的有效(這裡不拋異常不太能理解)
  • 自動掃描的Bean,不支持類名相同,但是包路徑不同的場景(會拋異常)

4. Import

在xml配置中,另一個常見的case就是引入另一個xml配置,在JavaConfig中代替的就是Import註解

@Configuration
@ComponentScan("com.git.hui.rabbit.spring")
@Import({DirectConsumerConfig.class, FanoutConsumerConfig.class, TopicConsumerConfig.class})
public class SpringConfig {
}

這個就等同於xml中常見的:

<import resource="service.xml" />

II. 實例測試

1. xml單測姿勢

上面說了用JavaConfig代替xml配置的方式,另一個關鍵的地方就是測試用例的寫法了,對於之前的xml,有兩種常見的使用姿勢

case1: 註解方式

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath*:*.xml")
public class BeanTest {
}

case2: 主動載入容器方式

private ServiceA serviceA;

@Before
public void init() {
    ApplicationContext apc = new ClassPathXmlApplicationContext("classpath:*.xml");
    serviceA = (ServiceA) apc.getBean("serviceA");
}

2. JavaConfig單測使用姿勢

那麼替換成JavaConfig的用法,也有兩種

case1: 註解方式,指定內部classes值

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class SprintUnit {
}

case2: 主動載入容器,改為AnnotationConfigApplicationContext

@Test
public void testServiceA() {
    ApplicationContext context = new AnnotationConfigApplicationContext(BeansConfiguration.class);
    ServiceA serviceA = (ServiceA) context.getBean("serviceA");
    serviceA.print();
}

III. 小結

1. 註解映射關係

JavaConfig方式基本上採用的是替換的思路來取代xml,即原xml中的一些東西,可以直接通過註解來代替,如

  • @Configuration 修飾類,與傳統的xml文件作用相同
  • @Bean註解,修飾方法,表示聲明一個Bean,與原來的xml中的 <bean> 標簽作用相同
  • @ComponentScan註解,自動掃描包,類似xml中的 <context:component-scan>
  • @Import註解,與xml中的<import>標簽類似,引入其他的配置信息

2. BeanName重名規則

在實際使用中,有一點需要額外註意,對於beanName相同的情況,通過測試的規則如下(沒有看源碼,不保證完全準確,僅為測試後得出的依據):

  • JavaConfig 定義的BeanName與自動掃描的BeanName衝突時,JavaConfig的定義的會被實例化
  • JavaConfig 中定義了BeanName相同的Bean時,優先定義的有效(這裡不拋異常不太能理解)
  • 自動掃描的Bean,不支持類名相同,但是包路徑不同的場景(會拋異常)

3. 測試姿勢

最簡單的就是修改原來的註解@ContextConfiguration中的值

@ContextConfiguration(classes = SpringConfig.class)

II. 其他

一灰灰Blog: https://liuyueyi.github.io/hexblog

一灰灰的個人博客,記錄所有學習和工作中的博文,歡迎大家前去逛逛

聲明

盡信書則不如,已上內容,純屬一家之言,因個人能力有限,難免有疏漏和錯誤之處,如發現bug或者有更好的建議,歡迎批評指正,不吝感激

掃描關註

  blogInfoV2.png
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本文分析Dubbo服務暴露的實現原理,併進行詳細的代碼跟蹤與解析。 ...
  • 這是我自己複習總結的,分享給大家,有不足和需要修改的地方,望大家指正,謝謝。 https://pan.baidu.com/s/14GrfC34CmjpnlND8MiT0YA ...
  • python中字元串對象提供了很多方法來操作字元串,功能相當豐富。 這些方法的使用說明見 "官方文檔:string methods" ,本文對它們進行詳細解釋,各位以後可將本文當作手冊。 這裡沒有模式匹配(正則)相關的功能。python中要使用模式匹配相關的方法操作字元串,需要 導入re模塊。關於正 ...
  • 1. python在讀取文件時,read(),readline()和readlines()有什麼區別? 舉例說明: 2、使用一行代碼輸出[1, 4, 9, 16, 25, 36, 49, 64, 81, 100] 3、編寫一個遞歸函數 ...
  • 假設現在已經打包了一個文件(1233444333),要將這個文件傳輸給另一方: 其中的上傳數據模塊和下載模塊可以單獨進行分裝後使用。 結果: ...
  • 時間序列深度學習:狀態 LSTM 模型預測太陽黑子 本文翻譯自《Time Series Deep Learning: Forecasting Sunspots With Keras Stateful Lstm In R》 "原文鏈接" 由於數據科學機器學習和深度學習的發展,時間序列預測在預測準確性方 ...
  • 前言 本人在通過《C語言程式設計:現代方法(第2版)》自學C語言時,發現國內並沒有該書完整的課後習題答案,所以就想把自己在學習過程中所做出的答案分享出來,以供大家參考。這些答案是本人自己解答,並參考GitHub上相關的分享和Chegg.com相關資料。因為並沒有權威的答案來源,所以可能會存在錯誤的地 ...
  • Java的數組以及操作數組一:什麼是數組;二:如何使用 Java 中的數組;三:使用迴圈操作 Java 中的數組;四:使用 Arrays 類操作 Java 中的數組;五:使用 foreach 操作數組;六:Java 中的二維數組; ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...