Spring設定類為什麼要分Full和Lite模式

2023-03-24 12:00:13

本文基於Spring 5.2.15-RELEASE

關於Spring設定類的Full模式和Lite模式,如果沒有仔細閱讀過原始碼或者官方檔案的話,估計很多人都不知道這個概念。所以我們先來解釋下這兩個概念。

概念解釋

@Configuration
public class DataSourceConfig {
 
	...
	@Bean
    public DataSource dataSource() {
    	...
        return dataSource;
    }
    @Bean(name = "transactionManager")
    public DataSourceTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource());
    }
	...
}

DataSourceConfig就是一個在Spring中非常常見的設定類,這個設定類本身也會被註冊成Spring容器中的一個Bean。上面這種設定方式預設的就是Full模式。為什麼預設是Full模式呢?其實祕密就藏在@Configuration這個註解中。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {

	@AliasFor(annotation = Component.class)
	String value() default "";
    // 預設會通過Cglib對產生Bean的方法進行增強
	boolean proxyBeanMethods() default true;
}

這個註解有一個屬性proxyBeanMethods。這個屬性的值預設是true,會通過Cglib對這個設定類進行增強,增強後這個設定類就顯得比較「重」,因此叫Full模式。如果我們將這個屬性設定成false的話,這個設定類產生的Bean就是原始的物件,比較「輕量級」,叫Lite模式。因此,Full模式和Lite模式最本質的區別是:設定類本身的Bean物件會不會被Cglib增強。

這裡再強調一個概念:Full模式和Lite模式都是說DataSourceConfig這個設定類是Full模式或者Lite模式。

Lite模式

上面提到,將@Configuration的proxyBeanMethods屬性設定成false就Lite模式的設定類,其實還有很多其他的場景也屬於Lite模式。整理下來,大致有以下這些場景:

  1. 類上標註有@Component註解
  2. 類上標註有@ComponentScan註解
  3. 類上標註有@Import註解
  4. 類上標註有@ImportResource註解
  5. 若類上沒有任何註解,但類記憶體在@Bean方法
  6. 標註有@Configuration(***proxyBeanMethods = false***)

自Spring5.2(對應Spring Boot 2.2.0)開始,內建的幾乎所有的@Configuration設定類都被修改為了@Configuration(proxyBeanMethods = false),以此來降低啟動時間,為Cloud Native繼續做準備。

優點

  • 執行時不再需要給對應類生成CGLIB子類,提高了執行效能,降低了啟動時間
  • 可以把該設定類當作一個普通類使用嘍:也就是說@Bean方法 可以是private、可以是final

缺點

  • 不能宣告@Bean之間的依賴,也就是說不能通過方法呼叫來依賴其它Bean
@Configuration(proxyBeanMethods = false)
public class DataSourceConfig {
 
	...
	@Bean
    public DataSource dataSource() {
    	...
        return dataSource;
    }
    @Bean(name = "transactionManager")
    public DataSourceTransactionManager transactionManager() {
        // 此處的dataSource()呼叫就是一次普通的方法呼叫,不會參照Spring IOC容器中的單列Bean
        return new DataSourceTransactionManager(dataSource());
    }
	...
}

這個問題可以這樣解決

@Configuration(proxyBeanMethods = false)
public class DataSourceConfig {
 
	...
	@Bean
    public DataSource dataSource() {
    	...
        return dataSource;
    }
    @Bean(name = "transactionManager")
    // 通過方法引數參照Spring容易中的Bean
    public DataSourceTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource;
    }
	...
}

Full模式

標註有@Configuration或者@Configuration(***proxyBeanMethods = true***)的類被稱為Full模式的設定類。

優點

  • 可以支援通過常規Java呼叫相同類的@Bean方法而保證是容器內的Bean;

缺點

  • 執行時會給該類生成一個CGLIB子類放進容器,有一定的效能、時間開銷;
  • 正因為被代理了,所以@Bean方法 不可以是private、不可以是final

簡單總結

Spring的設定類分成Full和Lite兩種模式。

在Lite模式下

  • 設定類本身不會被CGLIB增強,放進IoC容器內的就是設定類本身;
  • 對於內部類是沒有限制的:可以是Full模式或者Lite模式;
  • 設定類內部不能通過方法呼叫來處理依賴,否則每次生成的都是一個新範例而並非IoC容器內的單例
  • 設定類就是一普通類嘛,所以內部的@Bean方法可以使用private/final等進行修飾

在Full模式下

  • 設定類會被CGLIB增強(生成代理物件),放進IoC容器內的是代理物件;
  • 對於內部類是沒有限制:可以是Full模式或者Lite模式;
  • 該模式下,設定類內部可以通過方法呼叫來處理依賴,並且能夠保證是同一個範例,都指向IoC內的那個單例;
  • 該模式下,@Bean方法不能被private/final等進行修飾(因為方法需要被重寫,所以不能私有和final。defualt/protected/public都可以哦)

從上面的介紹可以看出來,Lite模式很大程度上是為了減少啟動開銷,提升程式的啟動速度。所以如果你對程式的啟動速度很敏感,就使用Lite模式,但是一定要記住此時的設定類已經不是經過Cglib增強過的類了。

參考