終於搞懂了 -Configuration 和 -Component 的區別 !
一句話概括就是 @Configuration
中所有帶 @Bean
註解的方法都會被動態代理,因此調用該方法返回的都是同一個實例。
理解:調用@Configuration
類中的 @Bean 註解的方法,返回的是同一個示例;而調用@Component
類中的@Bean
註解的方法,返回的是一個新的實例。
注意:上面說的調用,而不是從 spring 容器中獲取! 見最下面的示例 1 及 示例 2
下面看看實現的細節。
@Configuration 註解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
String value() default "";
}
從定義來看, @Configuration
註解本質上還是@Component
,因此 <context:component-scan/>
或者 @ComponentScan
都能處理@Configuration
註解的類。
@Configuration
標記的類必須符合下面的要求:
-
配置類必須以類的形式提供(不能是工廠方法返回的實例),允許通過生成子類在運行時增強(cglib 動態代理)。
-
配置類不能是 final 類(沒法動態代理)。
-
配置註解通常爲了通過
@Bean
註解生成 Spring 容器管理的類, -
配置類必須是非本地的(即不能在方法中聲明,不能是 private)。
-
任何嵌套配置類都必須聲明爲 static。
-
@Bean
方法可能不會反過來創建進一步的配置類(也就是返回的 bean 如果帶有@Configuration
,也不會被特殊處理,只會作爲普通的 bean)。
@Bean 註解方法執行策略
先給一個簡單的示例代碼:
@Configuration
public class MyBeanConfig {
@Bean
public Country country(){
return new Country();
}
@Bean
public UserInfo userInfo(){
return new UserInfo(country());
}
}
相信大多數人第一次看到上面 userInfo()
中調用 country()
時,會認爲這裏的 Country 和上面 @Bean
方法返回的 Country 可能不是同一個對象,因此可能會通過下面的方式來替代這種方式:
-
@Autowired
-
private Country country;
實際上不需要這麼做(後面會給出需要這樣做的場景),直接調用country()
方法返回的是同一個實例。
@Component 註解
@Component
註解並沒有通過 cglib 來代理@Bean
方法的調用,因此像下面這樣配置時,就是兩個不同的 country
。
@Component
public class MyBeanConfig {
@Bean
public Country country(){
return new Country();
}
@Bean
public UserInfo userInfo(){
return new UserInfo(country());
}
}
有些特殊情況下,我們不希望 MyBeanConfig
被代理(代理後會變成WebMvcConfig$$EnhancerBySpringCGLIB$$8bef3235293
)時,就得用 @Component
,這種情況下,上面的寫法就需要改成下面這樣:
@Component
public class MyBeanConfig {
@Autowired
private Country country;
@Bean
public Country country(){
return new Country();
}
@Bean
public UserInfo userInfo(){
return new UserInfo(country);
}
}
這種方式可以保證使用的同一個 Country 實例。
示例 1:調用 @Configuration 類中的 @Bean 註解的方法,返回的是同一個示例
第一個 bean 類
package com.xl.test.logtest.utils;
public class Child {
private String name = "the child";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
第二個 bean 類
package com.xl.test.logtest.utils;
public class Woman {
private String name = "the woman";
private Child child;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Child getChild() {
return child;
}
public void setChild(Child child) {
this.child = child;
}
}
@Configuration
類
package com.xl.test.logtest.utils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
@Configuration
//@Component
public class Human {
@Bean
public Woman getWomanBean() {
Woman woman = new Woman();
woman.setChild(getChildBean()); // 直接調用@Bean註解的方法方法getChildBean()
return woman;
}
@Bean
public Child getChildBean() {
return new Child();
}
}
測試類 I
本測試類爲一個配置類,這樣啓動項目是就可以看到測試效果,更加快捷;也可以使用其他方式測試見下面的測試類 II
package com.xl.test.logtest.utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
@Configuration
public class Man {
@Autowired
public Man(Woman wn, Child child) {
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
System.out.println(wn.getChild() == child ? "是同一個對象":"不是同一個對象");
}
}
啓動項目,查看輸出結果:
測試類 II
package com.xl.test.logtest.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.xl.test.logtest.utils.Child;
import com.xl.test.logtest.utils.Woman;
@RestController
public class LogTestController {
@Autowired
Woman woman ;
@Autowired
Child child;
@GetMapping("/log")
public String log() {
return woman.getChild() == child ? "是同一個對象":"不是同一個對象";
}
}
瀏覽器訪問項目,查看結果;輸入localhost:8080/log
示例 2 :調用 @Component 類中的 @Bean 註解的方法,返回的是一個新的實例。
測試代碼,只需要將@Configuration
改爲@Component
即可!其他的均不變
package com.xl.test.logtest.utils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
//@Configuration
@Component
public class Human {
@Bean
public Woman getWomanBean() {
Woman woman = new Woman();
woman.setChild(getChildBean()); // 直接調用@Bean註解的方法方法getChildBean()
return woman;
}
@Bean
public Child getChildBean() {
return new Child();
}
}
測試 :
控制檯和瀏覽器展示,均符合預期!
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/zfCt7tmRXBj_PqcwYsdHnA