Spring中@Configuration注解的Full模式和Lite模式詳解
前言
@Configuration 注解相信各位小伙伴經(jīng)常會用到,但是大家知道嗎,這個注解有兩種不同的模式,一種叫做 Full 模式,另外一種則叫做 Lite 模式。
準確來說,F(xiàn)ull 模式和 Lite 模式其實 Spring 容器在處理 Bean 時的兩種不同行為。
這兩種不同的模式在使用時候的表現(xiàn)完全不同,今天松哥就來和各位小伙伴捋一捋這兩種模式。
1. 概念梳理
首先我們先來看一下 Spring 官方文檔中對 Full 模式和 Lite 模式的一個介紹:
這個文檔主要講了這樣幾件事情:
- 我們可以通過在一個方法上添加 @Bean 注解,進而將該方法的返回值暴露給 Spring 容器,在這種場景下,@Bean 注解實際上就是一種通用的工廠方法機制。
- 當一個添加了 @Bean 注解的方法位于一個沒有添加 @Configuration 注解的類里邊時,那么這個添加了 @Bean 注解的方法在處理時就會按照 Lite 模式來處理。
- 當一個 Bean 被聲明在添加了 @Component 注解的類中,那么會按照 Lite 模式來處理。
- 當一個 Bean 被聲明在一個普通的類中時(plain old class),按照 Lite 模式來處理(這一點感覺和第二點差不多)。
- 在 Lite 模式下,@Bean 注解標記的方法最終不會被 CGLIB 進行代理,就是一個普通的工廠方法,因此,在 @Bean 標記的方法中,不能調(diào)用其他 @Bean 注解標記的方法,如果有需要,可以通過方法參數(shù)注入自己所需要的 Bean。
- 由于 Lite 模式下并不會使用 CGLIB,因此 @Bean 標記的方法可以是 final 類型的。
- 在大多數(shù)場景下,我們在一個 @Configuration 注解標記的類中,使用 @Bean 注解向 Spring 容器注冊一個 Bean,都是 Full 模式。
官網(wǎng)文檔的介紹還是有些抽象,接下來松哥通過具體的案例來和大家演示 Full 模式和 Lite 模式的差別。
2. Full 模式
先看 Full 模式,中文也可以稱之為 完整 模式,我們平時使用時,在一個配置類上添加 @Configuration 注解,且不添加任何額外屬性,這就是 Full 模式了。
Full 模式最大的特點是會給配置類通過 CGLIB 生成一個代理,所有被 @Bean 注解標記的方法將來都是通過代理方法進行調(diào)用。
假設(shè)我有如下配置類:
@Configuration public class JavaConfig { @Bean User user() { return new User(); } }
現(xiàn)在,我們?nèi)?Spring 容器獲取這個配置類:
public class JavaDemo { public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JavaConfig.class); JavaConfig config = ctx.getBean(JavaConfig.class); System.out.println("config.getClass() = " + config.getClass()); } }
打印結(jié)果如下:
大家看到,最終從 Spring 容器中拿到的 JavaConfig 實例并不是原始的 JavaConfig 對象,而是一個被代理的 JavaConfig 對象。
為什么要代理呢?肯定是為了實現(xiàn)某些功能。
大家看下面這個案例:
@Configuration public class JavaConfig { @Bean User user() { User user = new User(); user.setDog(dog()); return user; } @Bean Dog dog() { return new Dog(); } }
在 Full 模式下,在 user() 方法中調(diào)用 dog() 方法的時候,調(diào)用的是一個代理對象的 dog 方法,在這個代理對象的 dog 方法中,會首先去檢查 Spring 容器中是否存在 Dog 對象,如果存在,則直接使用 Spring 容器中的 dog 對象,就不會真正去執(zhí)行 dog 方法而獲取到一個新的 dog 對象了,如果 Spring 容器中不存在 dog 對象,才會創(chuàng)建新的 dog 對象出來。
一言以蔽之,在 Full 模式下,user 中的 dog 對象和 dog 方法注冊到 Spring 容器的 dog 對象是同一個。
在 Full 模式下,由于要給當前類生成代理,然后去代理 @Bean 注解標記的方法,因此,這些 @Bean 注解標記的方法不能是 final 或者 private 類型的,因為 final 或者 private 類型的方法無法被重寫,也就沒法生成代理對象,如果添加了 final 或者 private 修飾符,那么會拋出如下異常:
3. Lite 模式
再來看 Lite 模式,這種模式可以認為是一種精簡模式。
怎么開啟呢?我們可以去除配置類上的 @Configuration 注解,或者去除之后添加 @Component 注解,又或者使用 @ComponentScan、@ImportResource、@Import 等注解標記類,那么最終都是 Lite 模式:
@Component public class JavaConfig { @Bean final User user() { User user = new User(); user.setDog(dog()); return user; } @Bean Dog dog() { return new Dog(); } }
此時就是 Lite 模式,現(xiàn)在我們?nèi)?Spring 容器中獲取這個配置類:
public class JavaDemo { public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JavaConfig.class); JavaConfig config = ctx.getBean(JavaConfig.class); System.out.println("config.getClass() = " + config.getClass()); } }
最終打印結(jié)果如下:
大家看到,我們從 Spring 容器中拿到的就是原始的對象,而不是一個被代理過的對象。因此:
- 由于 @Bean 注解標記的方法沒有被代理,因此,該方法可以是 final 也可以是 private,運行時都不會報錯。
- 由于 @Bean 方法沒有被代理,因此在 user 方法中調(diào)用 dog 方法的時候,就直接調(diào)用了,這就導(dǎo)致 user 中的 dog 和最終 dog 方法注冊到 Spring 容器中的 dog 不是同一個。
針對第二點,如果想要確保 user 中的 dog 和 Spring 容器中的 dog 是同一個,那么可以通過參數(shù)將所需要的對象注入進來,類似下面這樣:
@Component public class JavaConfig { @Bean final User user(Dog dog) { User user = new User(); user.setDog(dog); return user; } @Bean Dog dog() { return new Dog(); } }
當 Spring 容器調(diào)用 user 方法初始化 User 對象時,發(fā)現(xiàn)該方法還有參數(shù),因此會去容器中查找這個參數(shù),找到了直接使用。
另外,我們也可以在類上添加 @Configuration 注解,但是通過修改屬性值來啟用 Lite 模式:
@Configuration(proxyBeanMethods = false) public class JavaConfig { @Bean final User user(Dog dog) { User user = new User(); user.setDog(dog); return user; } @Bean Dog dog() { return new Dog(); } }
如果設(shè)置了 proxyBeanMethods 屬性為 false,那么也就是 Lite 模式了,其實我們從屬性名稱上也能看出來端倪:是否代理 @Bean 注解標記的方法。
4. 小結(jié)
總結(jié)一下:
- Lite 模式下,配置類中的方法就是普通方法,可以是 final 類型,也可以是 private。
- Lite 模式下,不需要通過 CGLIB 生成動態(tài)代理類,所以啟動速度會快一些。
- Lite 模式下,一個 @Bean 方法調(diào)用另外一個 @Bean 方法,會導(dǎo)致同一個 Bean 被初始化兩次。
- Full 模式下,會給配置類生成一個動態(tài)代理類,配置類中的所有方法都將被動態(tài)代理,因此配置類中的方法不能是 final 或者 private 的。
- Full 模式下,一個 @Bean 方法調(diào)用另外一個 @Bean 方法,動態(tài)代理方法會先去容器中檢查是否存在該 Bean,如果存在,則直接使用容器中的 Bean,否則才會去創(chuàng)建新的對象。
日常開發(fā)中,我們使用較多的是 Full 模式。
到此這篇關(guān)于Spring中@Configuration注解的Full模式和Lite模式詳解的文章就介紹到這了,更多相關(guān)@Configuration注解的Full模式和Lite模式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Spring中的@Configuration詳解
- SpringBoot中@EnableAutoConfiguration和@Configuration的區(qū)別
- SpringBoot中的@Configuration注解詳解
- 關(guān)于Spring中的@Configuration中的proxyBeanMethods屬性
- 詳解@ConfigurationProperties如何裝載到Spring容器中
- 詳解Spring中@Component和@Configuration的區(qū)別
- Spring關(guān)于@Configuration配置處理流程
- 關(guān)于SpringBoot的@ConfigurationProperties注解和松散綁定、數(shù)據(jù)校驗
相關(guān)文章
將SpringBoot項目無縫部署到Tomcat服務(wù)器的操作流程
SpringBoot 是一個用來簡化 Spring 應(yīng)用初始搭建以及開發(fā)過程的框架,我們可以通過內(nèi)置的 Tomcat 容器來輕松地運行我們的應(yīng)用,本文給大家介紹 SpringBoot 項目部署到獨立 Tomcat 服務(wù)器的操作流程,需要的朋友可以參考下2024-05-05Spring Boot Logging Level設(shè)置為off時的Bug
這篇文章主要介紹了Spring Boot Logging Level設(shè)置為off時的Bug,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09springcloud?gateway實現(xiàn)簡易版灰度路由步驟詳解
這篇文章主要為大家介紹了springcloud?gateway實現(xiàn)簡易版灰度路由步驟詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-11-11