@Bean
和@Configuration
Spring中新的基於Java的配置的核心就是支持@Configuration
註解的類以及@Bean
註解的方法。java
@Bean
註解用來表示一個方法會實例化,配置,並初始化一個新的由Spring IoC容器所管理的對象。其做用等於XML配置中的<beans>
標籤下的<bean>
子標籤。開發者能夠用@Bean
註解來和任何的Spring@Component
來聯合使用,可是,最多見的狀況下,@Bean
註解仍是應用到註解了@Configuration
的類下面的。web
註解了@Configuration
的類就表示這個類的首要目的是用來管理Bean的配置的。並且,@Configuration
註解的類容許inter-bean
之類的依賴在類中經過方法調用來引用。最簡單的配置以下:spring
@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}
上面的配置將等價於以下的XML配置:編程
<beans>
<bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>
全
@Configuration
對比輕@Bean
模式
當@Bean
註解的方法聲明到了沒有配置@Configuration
的類中時,這些方法就會做爲輕@Bean
模式來處理。舉例來講,Bean方法若是聲明到了@Component
註解的類,或者普通的類之中,就是輕@Bean
模式。
和使用@Configuration
不一樣,輕@Bean
方法不可以聲明inter-bean
的依賴,一般一個@Bean
方法不該該調用另外一個@Bean
。
只有在註解了@Configuration
的類中使用@Bean
方法纔是全模式。這回防止不少@Bean
方法屢次調用,而且消除一些細微的bug。這些bug在輕模式很難被跟蹤。數組
@Bean
和@Configuration
註解將會在本文中詳細討論,首先咱們須要瞭解各類經過基於Java的配置建立容器的方法。markdown
本節描述的是使用Spring的AnnotationConfigApplicationContext
。在Spring 3.0後,各式各樣的ApplicationContext
實現均可用了,不僅是@Configuration
註解的類,還有一些註解了@Component
的組件類以及註解了JSR-330元數據的類等。app
當註解了@Configuration
的類做爲輸入的時候,註解了@Configuration
的類自己也會被註冊爲一個Bean,其中註解了@Bean
的方法都會被註冊爲Bean。ide
當註解了@Component
的類或者JSR-330的類做爲輸入的時候,他們一樣會被註冊爲Bean,並且能夠經過DI來注入到其餘的類裏面去,好比經過@Autowired
或者@Inject
註解。函數
在Spring使用XML做爲輸入的時候,實例的ApplicationContext
是ClassPathXmlApplicationContext
,而經過@Configuration
註解的類,實例化的ApplicationContext
是AnnotationConfigApplicationContext
。下面的代碼能夠徹底去除XML的配置就使用了Spring容器。url
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
MyService myService = ctx.getBean(MyService.class);
myService.doStuff();
}
如前面所述,AnnotationConfigApplicationContext
不只僅能夠和註解了@Configuration
的類配合,任何註解了@Component
或者是JSR-330的類一樣能夠做爲輸入來構造Sprign容器,好比:
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(MyServiceImpl.class, Dependency1.class, Dependency2.class);
MyService myService = ctx.getBean(MyService.class);
myService.doStuff();
}
上面這段代碼讓MyServiceImpl
,Dependency1
以及Dependency2
均可以使用Spring的依賴注入註解進行裝載,好比@Autowired
。
register(Class<?>..)
來構建容器AnnotationConfigApplicationContext
類除了經過類來初始化,也能夠經過無慘構造函數來進行構造,以後經過register()
方法來配置。這種方法在經過編程的方式來構建AnnotationConfigApplicationContext
的過程頗有用。
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(AppConfig.class, OtherConfig.class);
ctx.register(AdditionalConfig.class);
ctx.refresh();
MyService myService = ctx.getBean(MyService.class);
myService.doStuff();
}
使能組件掃描在前文中也略有說起,只須要在@Configuration
註解的類上配置便可:
@Configuration
@ComponentScan(basePackages = "com.acme")
public class AppConfig {
...
}
在XML等效的配置中,配置以下:
<beans> <context:component-scan base-package="com.acme"/> </beans>
在上面的例子中,com.acme
包中的內容會被掃描,來查找其中註解了@Component
的類,這些類都會被註冊爲Spring的Bean實例。AnnotationConfigApplicationContext
也能夠經過scan(String ...)
方法來經過函數調用的方式來進行掃描配置:
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.scan("com.acme");
ctx.refresh();
MyService myService = ctx.getBean(MyService.class);
}
@Congirufation
註解的類和@Component
註解的類都是掃描的候選者。在上面的例子中,若是AppConfig
類在com.acme
包中(或者是其子包之中),AppConfig
都會被scan
方法掃描到,在refresh()
方法調用後,其中的@Bean
註解的方法都會做爲Bean實例被註冊到容器之中。
WebApplicationContext
接口關於AnnotationConfigApplicationContext
的一個變化的版本就是AnnotationConfigWebApplicationContext
。這一實現能夠在配置Spring的ContextLoaderListener
這個Servlet的listener或者Spring MVC的DispatcherServlet的時候使用。下面就是web.xml中配置Spring MVC程序的一個配置:
<web-app>
<!-- Configure ContextLoaderListener to use AnnotationConfigWebApplicationContext instead of the default XmlWebApplicationContext -->
<context-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</context-param>
<!-- Configuration locations must consist of one or more comma- or space-delimited fully-qualified @Configuration classes. Fully-qualified packages may also be specified for component-scanning -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.acme.AppConfig</param-value>
</context-param>
<!-- Bootstrap the root application context as usual using ContextLoaderListener -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Declare a Spring MVC DispatcherServlet as usual -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- Configure DispatcherServlet to use AnnotationConfigWebApplicationContext instead of the default XmlWebApplicationContext -->
<init-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</init-param>
<!-- Again, config locations must consist of one or more comma- or space-delimited and fully-qualified @Configuration classes -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.acme.web.MvcConfig</param-value>
</init-param>
</servlet>
<!-- map all requests for /app/* to the dispatcher servlet -->
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
</web-app>
@Bean
註解@Bean
註解是一個方法級別的註解,和XML中的<bean />
標籤的功能一直。該註解支持<bean/>
的一些配置屬性,好比init-method
,destroy-method
,autowiring
以及name
等。
@Bean
註解能夠在註解了@Configuration
或者@Component
的類中使用。
經過將方法註解@Bean
便可聲明實例爲Bean。開發者經過這個方法將Bean註冊到ApplicationContext
,方法返回的類型就是Bean的類型。默認狀況下,方法的名字就是Bean默認的名字,參考以下Bean的聲明:
@Configuration
public class AppConfig {
@Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
}
上面的代碼徹底等價於一下XML:
<beans>
<bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>
兩種聲明均可以在ApplicationContext
中聲明一個名爲transferService
的Bean,實例的類型爲TransferServiceImpl
:
transferService -> com.acme.TransferServiceImpl
由@Bean
註解的方法能夠有任意數量的參數來描述其依賴。距離來講,若是TransferService
的其中一個依賴爲AccountRepository
的話,咱們能夠經過方法參數來構造:
@Configuration
public class AppConfig {
@Bean
public TransferService transferService(AccountRepository accountRepository) {
return new TransferServiceImpl(accountRepository);
}
}
解析的機制是和基於構造的依賴注入基本一致的,能夠參考前文Spring的依賴及其注入瞭解相關內容。
任何經過@Bean
註解了的方法返回的類,都支持常規的生命週期回調,並能夠經過使用JSR-250中的@PostContruct
以及@PreDestroy
註解。
基本的Spring生命週期也是一樣支持的,若是Bean實現了InitializingBean
,DisposableBean
或者是Lifecycle
接口的話,這些方法都會被容器調用。
標準的*Aware
接口好比說BeanFactoryAware
,BeanNameAware
,MessageSourceAware
,ApplicationContextAware
等接口也是支持的。
@Bean
也支持指定任意的初始化以及銷燬回調函數,跟Spring XML配置中的init-method
和destroy-method
屬性是一致的:
public class Foo {
public void init() {
// initialization logic
}
}
public class Bar {
public void cleanup() {
// destruction logic
}
}
@Configuration
public class AppConfig {
@Bean(initMethod = "init")
public Foo foo() {
return new Foo();
}
@Bean(destroyMethod = "cleanup")
public Bar bar() {
return new Bar();
}
}
默認狀況下,經過Java配置的Bean都會有一個
close
或者shutdown
方法來做爲自動的銷燬回調。若是開發者聲明瞭close
方法或者是shutdown
方法,可是不但願由容器來調用的話,能夠在註解中標記爲@Bean(destroyMethod="")
來代替默認的行爲。
固然,在上面的Foo
例子當中,也能夠直接調用init()
方法:
@Configuration
public class AppConfig {
@Bean
public Foo foo() {
Foo foo = new Foo();
foo.init();
return foo;
}
// ...
}
當直接使用Java的配置的時候,開發者能夠任意操做對象,而不只僅是依賴於容器的聲明週期
@Scope
註解開發者也能夠經過Java配置的方式來指定@Bean
的做用域,開發者可使用全部的標準的做用域,關於做用域的信息,能夠在Spring中Bean的做用域一文中瞭解。
默認的做用域是singleton
,可是開發者能夠經過@Scope
註解來覆蓋掉默認值:
@Configuration
public class MyConfiguration {
@Bean
@Scope("prototype")
public Encryptor encryptor() {
// ...
}
}
Sprnig針對那些做用域的依賴是經過代理來工做的。在XML中,能夠經過使用<aop:scoped-proxy/>
標籤來達到這個目的。經過Java配置的的@Scope
註解也提供等價的支持,默認的沒有代理配置爲ScopedProxyMode.NO
,也能夠指定爲ScopedProxyMode.TARGET_CLASS
或者ScopedProxyMode.INTERFACES
.
Java配置以下:
// an HTTP Session-scoped bean exposed as a proxy
@Bean
@SessionScope
public UserPreferences userPreferences() {
return new UserPreferences();
}
@Bean
public Service userService() {
UserService service = new SimpleUserService();
// a reference to the proxied userPreferences bean
service.setUserPreferences(userPreferences());
return service;
}
默認狀況下,配置類會讀取@Bean
方法中的方法的名字值做爲Bean的名字。固然能夠經過name
屬性來覆蓋這個功能。
@Configuration
public class AppConfig {
@Bean(name = "myFoo")
public Foo foo() {
return new Foo();
}
}
在前文之中有針對Bean名字的描述,有時候會給一個Bean多個名字,做爲Bean的別名,@Bean
註解的name
屬性也支持Spring數組類型的值:
@Configuration
public class AppConfig {
@Bean(name = { "dataSource", "subsystemA-dataSource", "subsystemB-dataSource" })
public DataSource dataSource() {
// instantiate, configure and return DataSource bean...
}
}
有的時候爲Bean提供額外的文本描述可讓別人更瞭解該Bean的做用。這一點尤爲在監視容器中的Bean的時候頗有效。
能夠經過使用@Description
註解來作到:
@Configuration
public class AppConfig {
@Bean
@Description("Provides a basic example of a bean")
public Foo foo() {
return new Foo();
}
}