spring學習(十一)——spring官方文件閱讀(5.0.7)——spring的@Bean與@Configuration註解
@Bean與@Configuration註解
@Bean註解用於方法上,返回的例項將由Spring IOC管理,當在@Configuration註解的類中使用@Bean註解時,@Bean相當於<bean/>元素,@Configuration相當於<beans>元素
@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}
上述程式碼等價於:
<beans> <bean id="myService" class="com.acme.services.MyServiceImpl"/> </beans>
當我們在非@Configuration的類中使用@Bean時,每個被@Bean註解的方法都相當於一個工廠方法,每次都會返回一個新的例項,原因是非@Configuration註解的類在使用@Bean註解時沒有使用CGLIB代理,不會被容器攔截,因此不會查詢容器中是否已經存在例項化的bean,直接進行例項化
使用AnnotationConfigApplicationContext初始化Spring容器
AnnotationConfigApplicationContext可以解析Spring以及JSR-330的註解,對於ClassPathCmlApplicationContext來說,XML檔案作為建構函式的引數,對於AnnotationConfigApplicationContext來說,使用@Configuration、@Component、JSR-330的類可以作為建構函式的引數,其餘的使用方式與ClassPathCmlApplicationContext一致,AnnotationConfigApplicationContext會將@Configuration註解的類解析為BeanDefinition,其內部用@Bean註解的方法也將被註冊為BeanDefinition
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
MyService myService = ctx.getBean(MyService.class);
myService.doStuff();
}
AnnotationConfigApplicationContext可以通過無參建構函式例項化,此時可以通過register()方法註冊BeanDefinition,需要用refresh()方法重新整理容器,以便完成註冊
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();
}
啟動元件掃描
有三種方式啟動spring的自動元件掃描:
1、使用@ComponentScan註解
@Configuration
@ComponentScan(basePackages = "com.acme")
public class AppConfig {
...
}
2、使用xml
<beans>
<context:component-scan base-package="com.acme"/>
</beans>
3、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);
}
使用@Bean註解
@Bean註解作用於方法上,支援設定<bean/>元素的某些屬性,例如init—method、destory—method、autowired以及name
@Bean註解可以在@Component、@Configuration註解的類中使用,也可以在普通的類中使用(不建議)
預設情況下,建立的bean的id和@Bean註解的method的名字一樣
@Configuration
public class AppConfig {
@Bean
public TransferServiceImpl transferService() {
return new TransferServiceImpl();
}
}
上述例子建立的bean的id為transferService
也可以用@Bean修飾返回介面型別的方法:
@Configuration
public class AppConfig {
@Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
}
容器接收到這個bean時,會認為它是TransferService型別,假設有某個Bean依賴於TransferServiceImpl,在例項化這個bean時,容器才會意識到名為transferService的bean的bean,其型別為TransferServiceImpl,對於某個繼承了許多介面的類來說,例項化該bean的方法的返回型別應該儘可能精確,至少和依賴注入點的型別相同
通過@Bean註解例項化的bean也可以具有依賴,將依賴作為方法的引數即可:
@Configuration
public class AppConfig {
@Bean
public TransferService transferService(AccountRepository accountRepository) {
return new TransferServiceImpl(accountRepository);
}
}
任何使用了@Bean註解的方法生成的bean都支援常規的生命週期回撥,也可以使用@PostConstruct、@PreDestroy註解,@Bean註解支援指定initialization和destruction生命週期回撥:
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();
}
}
預設情況下,bean的close或是shutdown方法在destruction回撥時將會被自動呼叫,如果我們不想呼叫,可以使用@Bean(destroyMethod="")
指定bean的生命週期
使用@Scope註解
@Configuration
public class MyConfiguration {
@Bean
@Scope("prototype")
public Encryptor encryptor() {
// ...
}
}
生命週期不一致的情況下可以考慮使用代理,@Bean註解也可以使用代理,通過設定ScopedProxyMode即可,預設情況下是不使用代理(ScopedProxyMode.No),也可以指定ScopedProxyMode.TARGET_CLASS
(CGLIB代理)或是 ScopedProxyMode.INTERFACES(JDK代理)
指定bean的名字
@Bean註解提供了name屬性用於設定bean的名字:
@Configuration
public class AppConfig {
@Bean(name = "myFoo")
public Foo foo() {
return new Foo();
}
}
指定bean的別名
有時候可以給定一個bean多個名字:
@Configuration
public class AppConfig {
@Bean(name = { "dataSource", "subsystemA-dataSource", "subsystemB-dataSource" })
public DataSource dataSource() {
// instantiate, configure and return DataSource bean...
}
}
bean的描述
有時候我們會對bean進行一些功能的描述:
@Configuration
public class AppConfig {
@Bean
@Description("Provides a basic example of a bean")
public Foo foo() {
return new Foo();
}
}
使用@Configuration註解
@Configuration用於類上,相當於xml中的<beans/>元素,通過public、@Bean註解的函式來宣告bean
@Bean註解的bean依賴於其他bean時,可以通過下列方式獲得容器管理的依賴bean:
@Configuration
public class AppConfig {
@Bean
public Foo foo() {
return new Foo(bar());
}
@Bean
public Bar bar() {
return new Bar();
}
}
這種宣告bean間依賴關係的方法只在@Bean方法在@Configuration類中宣告時有效。不能使用純@Component類宣告bean間依賴關係(獲得不是容器管理的bean,而是新的依賴bean)
@Confiuration註解的類會在啟動時成為CGLIB的子類,即使用CGLIB代理,在獲取bean時,首先會檢查容器中是否有現成的bean,如果沒有,則會呼叫代理的方法例項化bean,否則,直接進行依賴注入
@import註解
@import註解允許從其他的類中匯入@Bean定義:
@Configuration
public class ConfigA {
@Bean
public A a() {
return new A();
}
}
@Configuration
@Import(ConfigA.class)
public class ConfigB {
@Bean
public B b() {
return new B();
}
}
使用時,只需要在AnnotationConfigApplicationContext中註冊ConfigB即可:
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class);
// now both beans A and B will be available...
A a = ctx.getBean(A.class);
B b = ctx.getBean(B.class);
}
@import註解也支援匯入普通的java類,並將其宣告成一個bean
@Configuration註解的類會被註冊成bean,也可以使用@Autowired、@Value註解,由於@Configuration註解的類會比較早初始化,因此在其中使用@Autowired、@Value註解可能會導致某些類提前初始化,對於BeanPostProcessor和BeanFactoryPostProcessor類,由於需要比所有bean都先初始化,一般會用@Bean註解靜態方法生成例項
可以使用@Lazy實現懶載入,@DependsOn用於指定依賴項
有時根據系統環境,我們需要選擇啟動或是關閉某些@Configuration註解的類,可以使用@Profile或是@Conditional註解
結合java註解和xml配置
xml配置有時能完成java註解無法完成的事情,兩者使用的Application不同,我們可以結合兩者一起使用
一、如果我們打算使用ClassPathXmlApplicationContext,可以在xml中的<bean/>標籤描述@Configuration註解的類,並且使用<context:annotation-config/>
@Configuration
public class AppConfig {
@Autowired
private DataSource dataSource;
@Bean
public AccountRepository accountRepository() {
return new JdbcAccountRepository(dataSource);
}
@Bean
public TransferService transferService() {
return new TransferService(accountRepository());
}
}
system-test-config.xml
<beans>
<!-- enable processing of annotations such as @Autowired and @Configuration -->
<context:annotation-config/>
<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
<bean class="com.acme.AppConfig"/>
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
</beans>
jdbc.properties:
jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
jdbc.username=sa
jdbc.password=
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/com/acme/system-test-config.xml");
TransferService transferService = ctx.getBean(TransferService.class);
// ...
}
由於xml配置了識別註解,因此@Configuration註解會被識別出來,從而註冊其中的bean,由於@Component註解是@Configuration註解的元註解,因此在xml中使用 <context:component-scan/>也可以掃描到@Configuration註解
二、如果我們使用AnnotationConfigApplicationContext,可以通過@ImportResource註解引入xml配置
@Configuration
@ImportResource("classpath:/com/acme/properties-config.xml")
public class AppConfig {
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean
public DataSource dataSource() {
return new DriverManagerDataSource(url, username, password);
}
}
properties-config.xml
<beans>
<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
</beans>
jdbc.properties
jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
jdbc.username=sa
jdbc.password=
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
TransferService transferService = ctx.getBean(TransferService.class);
// ...
}