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

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

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

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處理或其餘約束。app

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

機器翻譯:當@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。this


相關定義

  • lite @Bean mode :當@Bean方法在沒有使用@Configuration註解的類中聲明時稱之爲lite @Bean mode
  • Full @Configuration:若是@Bean方法在使用@Configuration註解的類中聲明時稱之爲Full @Configuration

有何區別

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

舉個例子

有一普通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

相關文章
相關標籤/搜索