你真的懂Spring Java Config 嗎?Full @Configuration vs lite @Bean mode


Full @Configurationlite @Bean mode 是 Spring Java Config 中兩個非常有意思的概念。

先來看一下官方文檔關於這兩者的相關內容:

The @Bean methods in a regular Spring component are processed differently than their counterparts inside a Spring @Configuration class. The difference is that @Component classes are not enhanced with CGLIB to intercept the invocation of methods and fields. CGLIB proxying is the means by which invoking methods or fields within @Bean methods in @Configuration classes creates bean metadata references to collaborating objects. Such methods are not invoked with normal Java semantics but rather go through the container in order to provide the usual lifecycle management and proxying of Spring beans, even when referring to other beans through programmatic calls to @Bean methods. In contrast, invoking a method or field in a @Bean method within a plain @Component class has standard Java semantics, with no special CGLIB processing or other constraints applying.

機器翻譯結果:常規Spring組件中的@Bean方法與Spring @Configuration類中的對應方法處理方式不同。不同之處在於,@Component類沒有使用CGLIB增強來攔截方法和字段的調用。CGLIB代理是通過調用@Configuration類中的@Bean方法中的方法或字段來創建對協作對象的bean元數據引用的方法。這些方法不是用普通的Java語義調用的,而是通過容器來提供Spring bean通常的生命周期管理和代理,即使在通過對@Bean方法的編程調用引用其他bean時也是如此。相反,在普通@Component類中調用@Bean方法中的方法或字段具有標准的Java語義,不需要使用特殊的CGLIB處理或其他約束。

When @Bean methods are declared within classes that are not annotated with @Configuration, they are referred to as being processed in a “lite” mode. Bean methods declared in a @Component or even in a plain old class are considered to be “lite”, with a different primary purpose of the containing class and a @Bean method being a sort of bonus there. For example, service components may expose management views to the container through an additional @Bean method on each applicable component class. In such scenarios, @Bean methods are a general-purpose factory method mechanism. Unlike full @Configuration, lite @Bean methods cannot declare inter-bean dependencies. Instead, they operate on their containing component’s internal state and, optionally, on arguments that they may declare. Such a @Bean method should therefore not invoke other @Bean methods. Each such method is literally only a factory method for a particular bean reference, without any special runtime semantics. The positive side-effect here is that no CGLIB subclassing has to be applied at runtime, so there are no limitations in terms of class design (that is, the containing class may be final and so forth). In common scenarios, @Bean methods are to be declared within @Configuration classes, ensuring that “full” mode is always used and that cross-method references therefore get redirected to the container’s lifecycle management. This prevents the same @Bean method from accidentally being invoked through a regular Java call, which helps to reduce subtle bugs that can be hard to track down when operating in “lite” mode.

機器翻譯:當@Bean方法在沒有使用@Configuration注釋的類中聲明時,它們被稱為在“lite”模式下處理。在@Component中聲明的Bean方法,甚至在普通的舊類中聲明的Bean方法,都被認為是“lite”,包含類的主要目的不同,而@Bean方法在這里是一種附加功能。例如,服務組件可以通過每個適用組件類上的附加@Bean方法向容器公開管理視圖。在這種情況下,@Bean方法是一種通用的工廠方法機制。與完整的@Configuration不同,lite @Bean方法不能聲明bean之間的依賴關系。相反,它們對包含它們的組件的內部狀態進行操作,並可選地對它們可能聲明的參數進行操作。因此,這樣的@Bean方法不應該調用其他@Bean方法。每個這樣的方法實際上只是一個特定bean引用的工廠方法,沒有任何特殊的運行時語義。這里的積極副作用是在運行時不需要應用CGLIB子類,所以在類設計方面沒有限制(也就是說,包含的類可能是final類,等等)。在常見的場景中,@Bean方法將在@Configuration類中聲明,以確保始終使用“full”模式,並因此將交叉方法引用重定向到容器的生命周期管理。這可以防止通過常規Java調用意外調用相同的@Bean方法,這有助於減少在“lite”模式下操作時難以跟蹤的細微bug。


相關定義

  • lite @Bean mode :當@Bean方法在沒有使用@Configuration注解的類中聲明時稱之為lite @Bean mode
  • Full @Configuration:如果@Bean方法在使用@Configuration注解的類中聲明時稱之為Full @Configuration

有何區別

總結一句話,Full @Configuration中的@Bean方法會被CGLIB所代理,而 lite @Bean mode中的@Bean方法不會被CGLIB代理。

舉個例子

有一普通Java類,現在分別使用Full @Configurationlite @Bean mode這兩種模式注冊到Spring容器中。

public class User {
	public User() {
		System.out.println("user create... hashCode :" + this.hashCode());
	}
}

Full @Configuration

配置類:

@Configuration
public class AppConfig {

	@Bean
	public User user() {
		return new User();
	}

	@Bean
	public String name(User user) {
		System.out.println(user.hashCode());
		System.out.println(user().hashCode());
		return "123";
	}
}

主程序:

public class FullMain {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
		User user = context.getBean(User.class);
		System.out.println(user.hashCode());
		context.close();
	}
}

運行結果:

user create... hashCode :1709537756
1709537756
1709537756
1709537756

分析:@Bean是在使用了@Configuration注解的類上被聲明的,屬於Full @Configuration@Configuration類中的@Bean地方會被CGLIB進行代理。Spring會攔截該方法的執行,在默認單例情況下,容器中只有一個Bean,所以我們多次調用user()方法,獲取的都是同一個對象。

注意:被CGLIB的方法是不能被聲明為privatefinal,因為CGLIB是通過生成子類來實現代理的,privatefinal方法是不能被子類Override的,也就是說,Full @Configuration模式下,@Bean的方法是不能不能被聲明為privatefinal,不然在啟動時Spring會直接報錯。

lite @Bean mode

配置類:

@Component
public class LiteAppConfig {

	@Bean
	public User user() {
		System.out.println("user() 方法執行");
		return new User();
	}

	@Bean
	public String name(User user) {
		System.out.println("name(User user) 方法執行");
		System.out.println(user.hashCode());
		System.out.println("再次調用user()方法: " + user().hashCode());
		return "123";
	}
}

主程序:

public class LiteMain {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(LiteAppConfig.class);
		User user = context.getBean(User.class);
		System.out.println(user.hashCode());
		context.close();
	}
}

運行結果:

user() 方法執行
user create... hashCode :254413710
name(User user) 方法執行
254413710
user() 方法執行
user create... hashCode :1793329556
再次調用user()方法: 1793329556
254413710

分析:在lite @Bean mode模式下, @Bean方法不會被CGLIB代理,所以多次調用user()會生成多個user對象。

使用建議

為了防止出現一些奇怪的問題,建議都使用Full @Configuration


歡迎關注個人公眾號:
Coder小黑


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM