proxyBeanMethods 默認為true 表示這個類會被代理捐下,如果為false 這個類不會被代理其作用就類似于component注解奸柬。而如果為true那么他的作用類似事物的注解凡事帶有@Bean的方法將被代理具體的可以看下@Bean注解上的注釋就可以知道鸟缕,最后放了@Bean和@configuration 兩個注釋的翻譯大致能看懂从。
先說結(jié)果:如果設置為true 該類會被代理增強番甩,并且如果在@Bean 里的方法被另一個@bean調(diào)用的時候會進入到代理而不是進入到這個@Bean里缘薛,如果為false則表示這個類不會被代理宴胧,在@Bean里調(diào)用另一個@Bean 時會進入到這個@Bean方法里乞娄。這樣會造成什么樣的后果呢仪或?
那就是被調(diào)用的@Bean方法會有兩個對象。但是只有一個對象是spirng代理的到旦,另一個是自己創(chuàng)建的并沒有被spring代理這樣就會有一個問題厢绝,那就是這個對象里所有帶有spring相關的注解都會失效。舉個例子:
package org.demo.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
@Order(11)
//@Service
public class MyService1 implements MyServiceInterface{
@Autowired
public MyService myService;
//@PostConstruct
public void init(){
System.out.println("postConstruce:myService1");
}
public MyService1(MyService myService) {
this.myService = myService;
}
public MyService1() {
}
}
package org.demo.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.List;
//@Service
public class MyService {
@Autowired
private List<MyServiceInterface> myServiceInterfaceList;
private String name;
private Long height;
@Autowired
public MyService1 myService1;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getHeight() {
return height;
}
public void setHeight(Long height) {
this.height = height;
}
public MyService() {
}
//@PostConstruct
public void send(){
System.out.println("myServicePostConstruct");
}
public void toList(){
myServiceInterfaceList.stream().forEach(myServiceInterface -> System.out.println(myServiceInterface.getClass()));
}
public MyService(MyService1 myService1) {
this.myService1 = myService1;
}
}
package org.demo;
import org.demo.config.MyConfig;
import org.demo.service.MyService;
import org.demo.service.MyService1;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Application {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
annotationConfigApplicationContext.register(MyConfig.class);
annotationConfigApplicationContext.refresh();
MyService myService = annotationConfigApplicationContext.getBean(MyService.class);
System.out.println("mian:"+myService);
MyService1 myService1 = annotationConfigApplicationContext.getBean(MyService1.class);
System.out.println("mian:"+myService1.myService);
System.out.println("mian:"+myService);
}
}
package org.demo.config;
import org.demo.service.MyService;
import org.demo.service.MyService1;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import javax.annotation.Resource;
@ComponentScan("org.demo")
@Configuration(proxyBeanMethods = true)
@DependsOn(value ="staticProperties")
public class MyConfig {
@Resource
StaticProperties staticProperties;
@Bean
public MyService myService(){
MyService myService = new MyService();
myService.setHeight(staticProperties.getHeight());
myService.setName(staticProperties.getName());
System.out.println("config:myservice:"+myService.toString());
return myService;
}
@Bean
public MyService1 myService1(){
MyService myService =myService();
System.out.println("config:myservice1:"+myService.toString());
MyService1 myService1 = new MyService1(myService);
return myService1;
}
}
myService1.myService!=myService
這里的不等于是在spring 創(chuàng)建bean的時候其實用戶調(diào)用的時候仍然是同一個對象
下圖的調(diào)用棧顯示spirng 會進入intercept 這個方法
方法的注釋:
運行結(jié)果:
如果我將proxyBeanMethods=false 時再看下調(diào)用棧沪停。下圖所示這里是直接進入了原方法沒有進入代理的方法众辨。
下面在看下輸出的日志:config:是MyConfig類 里輸出的
main:main方法調(diào)用getBean后輸出的
這里為啥會main 方法里和config類里的日志不一樣呢鹃彻,那是因為myService1()方法會調(diào)用myService()方法new出來新的對象
但是在對象初始化的時候因為有注解@Autowired 導致我們自己放入的對象被覆蓋团赁;這就導致我們new 出來的對象沒有用了
如果我輸出myService1.myService.myService1 會是什么樣的結(jié)果欢摄?
package org.demo.config;
import org.demo.service.MyService;
import org.demo.service.MyService1;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import javax.annotation.Resource;
@ComponentScan("org.demo")
@Configuration(proxyBeanMethods = true)
@DependsOn(value ="staticProperties")
public class MyConfig {
@Resource
StaticProperties staticProperties;
@Bean
public MyService myService(){
MyService myService = new MyService();
myService.setHeight(staticProperties.getHeight());
myService.setName(staticProperties.getName());
System.out.println("config:myservice:"+myService.toString());
return myService;
}
@Bean
public MyService1 myService1(){
//這里會調(diào)用的是myService() 會new 出來一個新對象
MyService myService =myService();
System.out.println("config:myservice1:"+myService.toString());
MyService1 myService1 = new MyService1(myService);
return myService1;
}
}
最后放@Bean的注釋,大致還能看
org.springframework.context.annotation @Target({ElementType.METHOD,ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public interface Bean
extends annotation.Annotation
指示一個方法產(chǎn)生一個由 Spring 容器管理的 bean矗钟。
概述
此注釋的屬性的名稱和語義有意類似于 Spring XML 模式中<bean/>元素的名稱和語義吨艇。 例如:
@Bean
public MyBean myBean() {
// instantiate and configure MyBean obj
return obj;
}
豆名
雖然name屬性可用,但確定 bean 名稱的默認策略是使用@Bean方法的名稱疮跑。 這既方便又直觀凸舵,但如果需要顯式命名祖娘,可以使用name屬性(或其別名value )。 還要注意name接受一個字符串數(shù)組啊奄,允許為單個 bean 使用多個名稱(即一個主要 bean 名稱加上一個或多個別名)渐苏。
@Bean({"b1", "b2"}) // bean available as 'b1' and 'b2', but not 'myBean'
public MyBean myBean() {
// instantiate and configure MyBean obj
return obj;
}
Profile、Scope菇夸、Lazy琼富、DependsOn、Primary庄新、Order
請注意, @Bean注釋不為配置文件、范圍、惰性暇仲、依賴或主要提供屬性斥滤。 相反挑胸,它應該與@Scope 、 @Lazy 戳粒、 @DependsOn和@Primary注釋結(jié)合使用來聲明這些語義苹祟。 例如:
@Bean
@Profile("production")
@Scope("prototype")
public MyBean myBean() {
// instantiate and configure MyBean obj
return obj;
}
上述注釋的語義與它們在組件類級別的使用相匹配: @Profile允許選擇性地包含某些 bean。 @Scope將 bean 的作用域從單例更改為指定的作用域和措。 @Lazy只有在默認單例范圍的情況下才有實際效果贫母。 @DependsOn強制在創(chuàng)建此 bean 之前創(chuàng)建特定的其他 bean誓酒,以及 bean 通過直接引用表示的任何依賴項靡狞,這通常有助于單例啟動温兼。 如果需要注入單個目標組件但多個 bean 按類型匹配全释, @Primary是一種在注入點級別解決歧義的機制。
此外槽惫, @Bean方法還可以聲明限定符注解和@Order值,在注入點解析過程中被考慮,就像相應組件類上的相應注解一樣疗认,但每個 bean 定義可能非常獨立(如果多個定義具有相同的豆類)。 限定符在初始類型匹配后縮小候選集亡笑; 在集合注入點的情況下厕九,順序值確定解析元素的順序(多個目標 bean 按類型和限定符匹配)谷羞。
注意: @Order值可能會影響注入點的優(yōu)先級帝火,但請注意,它們不會影響單例啟動順序,這是由依賴關系和@DependsOn聲明確定的正交問題父能,如上所述坡慌。 此外狸吞, javax.annotation.Priority在此級別不可用,因為它不能在方法上聲明指煎; 它的語義可以通過@Order值結(jié)合@Primary在每個類型的單個bean 上建模蹋偏。
@Configuration類中的@Bean方法
通常, @Bean方法在@Configuration類中聲明至壤。 在這種情況下威始,bean 方法可以通過直接調(diào)用來引用同一個類中的其他@Bean方法。 這確保了 bean 之間的引用是強類型和可導航的。 這種所謂的“豆間引用”保證尊重范圍和 AOP 語義,就像getBean()查找一樣铝量。 這些是從原始“Spring JavaConfig”項目中已知的語義赏廓,它們需要在運行時對每個此類配置類進行 CGLIB 子類化。 因此, @Configuration類及其工廠方法在此模式下不得標記為 final 或 private。 例如:
@Configuration
public class AppConfig {
@Bean
public FooService fooService() {
return new FooService(fooRepository());
}
@Bean
public FooRepository fooRepository() {
return new JdbcFooRepository(dataSource());
}
// ...
}
@Bean精簡模式
@Bean方法也可以在沒有用@Configuration注釋的類中聲明。 例如随静,bean 方法可以在@Component類中聲明,甚至可以在普通的舊類中聲明吗讶。 在這種情況下燎猛, @Bean方法將以所謂的“精簡”模式進行處理。
精簡模式下的 Bean 方法將被容器視為普通工廠方法(類似于 XML 中的factory-method聲明)照皆,并正確應用范圍和生命周期回調(diào)重绷。 在這種情況下,包含類保持不變纵寝,并且包含類或工廠方法沒有異常約束论寨。
與@Configuration類中 bean 方法的語義相反星立,精簡模式不支持“bean 間引用” 。 相反葬凳,當一個@Bean -method 在精簡模式下調(diào)用另一個@Bean -method 時绰垂,該調(diào)用是標準的 Java 方法調(diào)用; Spring 不會通過 CGLIB 代理攔截調(diào)用火焰。 這類似于@Transactional方法間調(diào)用劲装,在代理模式下,Spring 不攔截調(diào)用——Spring 只在 AspectJ 模式下這樣做昌简。
例如:
@Component
public class Calculator {
public int sum(int a, int b) {
return a+b;
}
@Bean
public MyBean myBean() {
return new MyBean();
}
}
引導
見@ Configuration的javadoc進一步詳情占业,包括如何使用來引導容器AnnotationConfigApplicationContext和朋友。
BeanFactoryPostProcessor返回@Bean方法
必須特別考慮返回 Spring BeanFactoryPostProcessor ( BFPP ) 類型的@Bean方法纯赎。 因為BFPP對象必須在容器生命周期的早期實例化谦疾,所以它們會干擾@Configuration類中的@Autowired 、 @Value和@PostConstruct等注釋的處理犬金。 為避免這些生命周期問題念恍, BFPP返回@Bean方法標記為static 。 例如:
@Bean
public static PropertySourcesPlaceholderConfigurer pspc() {
// instantiate, configure and return pspc ...
}
通過將此方法標記為static 晚顷,可以在不導致其聲明的@Configuration類的實例化的情況下調(diào)用它峰伙,從而避免上述生命周期沖突。 但是請注意该默, @Bean 瞳氓, static @Bean方法不會針對作用域和 AOP 語義進行增強。 這適用于BFPP情況栓袖,因為它們通常不被其他@Bean方法引用匣摘。 提醒一下,對于具有可分配給BeanFactoryPostProcessor的返回類型的任何非靜態(tài)@Bean方法裹刮,都將發(fā)出 WARN 級別的日志消息恋沃。
自從:
3.0
也可以看看:
Configuration 、 Scope 必指、 DependsOn 、 Lazy 恕洲、 Primary 塔橡、 org.springframework.stereotype.Component 、 org.springframework.beans.factory.annotation.Autowired 霜第、 org.springframework.beans.factory.annotation.Value
spring.spring-context.main
org.springframework.context.annotation @Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public interface Configuration
extends annotation.Annotation
表示一個類聲明了一個或多個@Bean方法葛家,并且可能會被 Spring 容器處理以在運行時為這些 bean 生成 bean 定義和服務請求,例如:
@Configuration
public class AppConfig {
@Bean
public MyBean myBean() {
// instantiate, configure and return bean ...
}
}
引導@Configuration類
通過AnnotationConfigApplicationContext
@Configuration類通常使用AnnotationConfigApplicationContext或其支持 web 的變體AnnotationConfigWebApplicationContext引導泌类。 前者的一個簡單示例如下:
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(AppConfig.class);
ctx.refresh();
MyBean myBean = ctx.getBean(MyBean.class);
// use myBean ...
有關詳細信息癞谒,請參閱AnnotationConfigApplicationContext javadocs,有關Servlet容器中的 Web 配置說明,請參閱AnnotationConfigWebApplicationContext 弹砚。
通過 Spring <beans> XML
作為直接針對AnnotationConfigApplicationContext注冊@Configuration類的替代方法双仍,可以將@Configuration類聲明為 Spring XML 文件中的普通<bean>定義:
<beans>
<context:annotation-config/>
<bean class="com.acme.AppConfig"/>
</beans>
在上面的示例中,需要<context:annotation-config/>以啟用ConfigurationClassPostProcessor和其他與注釋相關的后處理器桌吃,這些后處理器有助于處理@Configuration類朱沃。
通過元件掃描
@Configuration使用@Component元注釋,因此@Configuration類是組件掃描的候選者(通常使用 Spring XML 的<context:component-scan/>元素)茅诱,因此也可以像任何常規(guī)@Component一樣利用@Autowired / @Inject @Component 逗物。 特別是,如果存在單個構造函數(shù)瑟俭,自動裝配語義將透明地應用于該構造函數(shù):
@Configuration
public class AppConfig {
private final SomeBean someBean;
public AppConfig(SomeBean someBean) {
this.someBean = someBean;
}
// @Bean definition using "SomeBean"
}
@Configuration類不僅可以使用組件掃描進行引導翎卓,還可以使用@ComponentScan注釋自己配置組件掃描:
@Configuration
@ComponentScan("com.acme.app.services")
public class AppConfig {
// various @Bean definitions ...
}
有關詳細信息,請參閱@ComponentScan javadocs摆寄。
使用外化值
使用Environment API
可以通過將 Spring org.springframework.core.env.Environment注入@Configuration類來查找外化值——例如失暴,使用@Autowired注釋:
@Configuration
public class AppConfig {
@Autowired Environment env;
@Bean
public MyBean myBean() {
MyBean myBean = new MyBean();
myBean.setName(env.getProperty("bean.name"));
return myBean;
}
}
通過Environment解析的屬性駐留在一個或多個“屬性源”對象中, @Configuration類可以使用@PropertySource注釋將屬性源貢獻給Environment對象:
@Configuration
@PropertySource("classpath:/com/acme/app.properties")
public class AppConfig {
@Inject Environment env;
@Bean
public MyBean myBean() {
return new MyBean(env.getProperty("bean.name"));
}
}
有關更多詳細信息椭迎,請參閱Environment和@PropertySource javadocs锐帜。
使用@Value注釋
可以使用@Value注釋將外部化的值注入到@Configuration類中:
@Configuration
@PropertySource("classpath:/com/acme/app.properties")
public class AppConfig {
@Value("${bean.name}") String beanName;
@Bean
public MyBean myBean() {
return new MyBean(beanName);
}
}
這種方法通常與 Spring 的PropertySourcesPlaceholderConfigurer結(jié)合使用,它可以通過<context:property-placeholder/>在 XML 配置中自動啟用畜号,或者通過專用的static @Bean方法在@Configuration類中顯式@Bean (請參閱“關于 BeanFactoryPostProcessor-returning @Bean方法”的@Bean的 javadocs 的詳細信息)缴阎。 但是請注意,通常僅當您需要自定義配置(例如占位符語法等)時简软,才需要通過static @Bean方法顯式注冊PropertySourcesPlaceholderConfigurer 蛮拔。具體來說,如果沒有 bean 后處理器(例如PropertySourcesPlaceholderConfigurer )已注冊ApplicationContext的嵌入值解析器痹升,Spring 將注冊一個默認的嵌入值解析器建炫,它根據(jù)Environment注冊的屬性源解析占位符。 請參閱以下有關使用@ImportResource使用 Spring XML 組合@Configuration類的部分疼蛾; 請參閱@Value javadocs肛跌; 有關使用BeanFactoryPostProcessor類型(例如PropertySourcesPlaceholderConfigurer詳細信息,請參閱@Bean javadocs察郁。
組合@Configuration類
使用@Import注釋
@Configuration類可以使用@Import注釋組成衍慎,類似于<import>在 Spring XML 中的工作方式。 因為@Configuration對象在容器內(nèi)作為 Spring bean 進行管理皮钠,所以可以注入導入的配置——例如稳捆,通過構造函數(shù)注入:
@Configuration
public class DatabaseConfig {
@Bean
public DataSource dataSource() {
// instantiate, configure and return DataSource
}
}
@Configuration
@Import(DatabaseConfig.class)
public class AppConfig {
private final DatabaseConfig dataConfig;
public AppConfig(DatabaseConfig dataConfig) {
this.dataConfig = dataConfig;
}
@Bean
public MyBean myBean() {
// reference the dataSource() bean method
return new MyBean(dataConfig.dataSource());
}
}
現(xiàn)在, AppConfig和導入的DatabaseConfig都可以通過僅針對 Spring 上下文注冊AppConfig來引導:
new AnnotationConfigApplicationContext(AppConfig.class);
使用@Profile注釋
@Configuration類可以用@Profile注釋標記麦轰,以表明只有在給定的一個或多個配置文件處于活動狀態(tài)時才應該處理它們:
@Profile("development")
@Configuration
public class EmbeddedDatabaseConfig {
@Bean
public DataSource dataSource() {
// instantiate, configure and return embedded DataSource
}
}
@Profile("production")
@Configuration
public class ProductionDatabaseConfig {
@Bean
public DataSource dataSource() {
// instantiate, configure and return production DataSource
}
}
或者乔夯,您也可以在@Bean方法級別聲明配置文件條件 - 例如砖织,對于同一配置類中的替代 bean 變體:
@Configuration
public class ProfileDatabaseConfig {
@Bean("dataSource")
@Profile("development")
public DataSource embeddedDatabase() { ... }
@Bean("dataSource")
@Profile("production")
public DataSource productionDatabase() { ... }
}
有關更多詳細信息,請參閱@Profile和org.springframework.core.env.Environment javadocs末荐。
使用@ImportResource注釋的 Spring XML
如上所述侧纯, @Configuration類可以在 Spring XML 文件中聲明為常規(guī) Spring <bean>定義。 也可以使用@ImportResource注釋將 Spring XML 配置文件導入到@Configuration類中鞠评。 可以注入從 XML 導入的 Bean 定義——例如茂蚓,使用@Inject注釋:
@Configuration
@ImportResource("classpath:/com/acme/database-config.xml")
public class AppConfig {
@Inject DataSource dataSource; // from XML
@Bean
public MyBean myBean() {
// inject the XML-defined dataSource bean
return new MyBean(this.dataSource);
}
}
使用嵌套的@Configuration類
@Configuration類可以相互嵌套,如下所示:
@Configuration
public class AppConfig {
@Inject DataSource dataSource;
@Bean
public MyBean myBean() {
return new MyBean(dataSource);
}
@Configuration
static class DatabaseConfig {
@Bean
DataSource dataSource() {
return new EmbeddedDatabaseBuilder().build();
}
}
}
在引導這種安排時剃幌,只需要針對應用程序上下文注冊AppConfig 聋涨。 由于是嵌套的@Configuration類, DatabaseConfig將自動注冊负乡。 當AppConfig和DatabaseConfig之間的關系已經(jīng)隱式明確時牍白,這避免了使用@Import注釋的需要。
另請注意抖棘,嵌套的@Configuration類可以與@Profile注釋一起使用茂腥,以向封閉的@Configuration類提供相同 bean 的兩個選項。
配置延遲初始化
默認情況下切省, @Bean方法將在容器引導時急切地實例化最岗。 為了避免這種情況, @Configuration @Lazy可以與@Lazy注釋結(jié)合使用朝捆,以指示類中聲明的所有@Bean方法默認是延遲初始化的般渡。 請注意, @Lazy也可以用于單獨的@Bean方法芙盘。
對@Configuration類的測試支持
spring-test模塊中可用的 Spring TestContext 框架提供了@ContextConfiguration注釋驯用,它可以接受@ContextConfiguration件類引用——通常是@Configuration或@Component類。
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {AppConfig.class, DatabaseConfig.class})
public class MyTests {
@Autowired MyBean myBean;
@Autowired DataSource dataSource;
@Test
public void test() {
// assertions against myBean ...
}
}
有關詳細信息儒老,請參閱TestContext 框架 參考文檔蝴乔。
使用@Enable注釋啟用內(nèi)置 Spring 功能
Spring 的特性,例如異步方法執(zhí)行驮樊、計劃任務執(zhí)行薇正、注解驅(qū)動的事務管理,甚至 Spring MVC 都可以從@Configuration類使用它們各自的“ @Enable ”注解來啟用和配置囚衔。 有關詳細@EnableAsync 铝穷,請參閱@EnableAsync 、 @EnableScheduling 佳魔、 @EnableTransactionManagement 、 @EnableAspectJAutoProxy和@EnableWebMvc 晦炊。
創(chuàng)作@Configuration類時的約束
配置類必須作為類提供(即不是作為從工廠方法返回的實例)鞠鲜,允許通過生成的子類進行運行時增強宁脊。
配置類必須是非最終的(允許在運行時使用子類),除非proxyBeanMethods標志設置為false在這種情況下不需要運行時生成的子類贤姆。
配置類必須是非本地的(即不能在方法中聲明)榆苞。
任何嵌套的配置類都必須聲明為static 。
@Bean方法可能不會反過來創(chuàng)建更多的配置類(任何此類實例都將被視為常規(guī) bean霞捡,它們的配置注釋仍然未被檢測到)坐漏。
自從:
3.0
也可以看看:
Bean 、 Profile 碧信、 Import 赊琳、 ImportResource 、 ComponentScan 砰碴、 Lazy 躏筏、 PropertySource 、 AnnotationConfigApplicationContext 呈枉、 ConfigurationClassPostProcessor 趁尼、 org.springframework.core.env.Environment 、 org.springframework.test.context.ContextConfiguration
spring.spring-context.main