前言
Spring3.0之前要使用Spring必須要有一個xml配置檔案,而Spring3.0之後注解慢慢登上舞台,通過注解@Configuration和@Bean可以完全搞定。此時,注解和xml配置形成了互相協作與競争的關系。随着Springboot的推廣,注解的使用在Spring中大放光彩,xml的輝煌一去不返。通過注解,簡化了配置,提升了編碼效率。
Spring 3.0新增了另外兩個實作類:AnnotationConfigApplicationContext 和 AnnotationConfigWebApplicationContext。它們是為注解而生,直接依賴于注解作為容器配置資訊來源的IoC容器初始化類。AnnotationConfigWebApplicationContext是AnnotationConfigApplicationContext的web版本,其用法與後者相比幾乎沒有什麼差别。
今天這篇文章帶大家學習@Configuration和@Bean的使用,并通過具體的執行個體體驗一下注解的友善快捷。如果你的項目中還未曾使用此類注解,說明你的技術棧已經在被淘汰的邊緣。
基于Java類的配置選項
Spring 3.0引入了注解,配置檔案的載體就從xml檔案轉換為了Java類,Java類就是一個普通的類,除了命名建議以“**Config”結尾友善識别外,Spring對其有一定的約定條件。
- 配置類不能是 final 類(沒法動态代理)。
- 配置類必須是非本地的(即不能将配置類定義在其他類的方法内部,不能是private)。
- 配置類必須有一個無參構造函數。
基本使用方法
符合上述條件的類,就可以使用@Configuration來進行注解,表示這個類可以使用Spring IoC容器作為bean定義的來源。@Bean注解在該類的方法上,AnnotationConfigApplicationContext将配置類中标注了@Bean的方法的傳回值識别為Spring Bean,并注冊到容器中,歸入IoC容器管理。
@Configuration的作用等價于XML配置中的标簽,@Bean的作用等價于XML配置中的标簽。下面代碼完成了一個簡單的示例。
-
@Configuration
-
public class DataSourceConfig {
-
@Bean
-
public MysqlDataSource mysqlDataSource() {
-
return new MysqlDataSource();
-
}
-
@Bean(name = "oracleDataSource")
-
public OracleDataSource oracleDataSource() {
-
return new OracleDataSource();
-
}
-
}
Spring在解析該類時,會識别出标注@Bean的所有方法,執行并将方法的傳回值(MysqlDataSource和OracleDataSource對象)注冊到IoC容器中。預設情況下,方法名即為Bean的名字。與以上配置等價的XML配置如下:
-
<bean id="mysqlDataSource" class="**.MysqlDataSource"/>
-
<bean id="oracleDataSource" class="**.OracleDataSource"/>
@Configuration的屬性
@Configuration的定義代碼:
-
@Target({ElementType.TYPE})
-
@Retention(RetentionPolicy.RUNTIME)
-
@Documented
-
@Component
-
public @interface Configuration {
-
String value() default "";
-
}
@Configuration注解本身定義時被@Component标注了,是以本質上來說@Configuration也是一個@Component,隻不過我們在具體使用的過程中基本用不到它的執行個體化對象。
@Bean屬性
@Bean注解的具體代碼定義:
-
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
-
@Retention(RetentionPolicy.RUNTIME)
-
@Documented
-
public @interface Bean {
-
@AliasFor("name")
-
String[] value() default {};
-
@AliasFor("value")
-
String[] name() default {};
-
Autowire autowire() default Autowire.NO;
-
String initMethod() default "";
-
String destroyMethod() default "(inferred)";
-
}
可以看出@Bean具有以下屬性:
- name :指定一個或者多個Bean的名字。等價于XML配置中的name屬性,示例中的@Bean(name = "oracleDataSource")。
- initMethod:容器在初始化完Bean之後,會調用該屬性指定的方法。等價于XML配置中的init-method屬性。
- destroyMethod:該屬性與initMethod功能相似,在容器銷毀Bean之前,會調用該屬性指定的方法。等價于XML配置中的destroy-method屬性。
- autowire:指定Bean屬性的自動裝配政策,取值是Autowire類型的三個靜态屬性。Autowire.BYNAME,Autowire.BYTYPE,Autowire.NO。與XML配置中的autowire屬性的取值相比,少了constructor,因為 constructor在這裡已經沒有意義了。
@Bean預設是單例模式,并且沒有提供指定作用域的屬性,可以通過@Scope來實作該功能。
-
@Bean
-
@Scope("prototype")
-
public MysqlDataSource mysqlDataSource() {
-
return new MysqlDataSource();
-
}
Spring掃描加載
當配置完Spring掃描指定包及其子包中的類時,會識别所有标記了@Component、@Controller、@Service、@Repository注解的類,由于@Configuration注解本身也用@Component标注了,Spring将能夠識别出 @Configuration标注類。
執行個體
現在對上面的示例進行單元測試,其中MysqlDataSource和OracleDataSource類中分别提供了列印日志的方法:
-
public void print(){
-
System.out.println("I am MysqlDataSource!");
-
}
-
和
-
public void print(){
-
System.out.println("I am OracleDataSource!");
-
}
指定單元測試的代碼如下:
-
public class ConfigBeanTest {
-
@Test
-
public static void testGetBean() {
-
ApplicationContext ctx = new AnnotationConfigApplicationContext(DataSourceConfig.class);
-
MysqlDataSource mysqlDataSource = ctx.getBean(MysqlDataSource.class);
-
mysqlDataSource.print();
-
}
-
}
當執行單元測試,成功列印出:
-
I am MysqlDataSource!
實戰技巧
如果我們在DataSourceConfig中再添加一個方法,這個方法用到了前面執行個體化的兩個bean對象,那麼該如何處理?正常的想法是在DataSourceConfig中添加如下代碼,然後再直接使用屬性:
-
@Autowired
-
private MysqlDataSource mysqlDataSource;
其實不必如此,直接調用方法mysqlDataSource()方法即可。比如,需要新增下面類的執行個體化:
-
public class CommonDataSource {
-
private MysqlDataSource mysqlDataSource;
-
private OracleDataSource oracleDataSource;
-
// 省略getter/setter方法
-
}
那麼,我們隻用在DataSourceConfig中添加如下代碼即可:
-
@Bean
-
public CommonDataSource commonDataSource() {
-
CommonDataSource commonDataSource = new CommonDataSource();
-
commonDataSource.setMysqlDataSource(mysqlDataSource());
-
commonDataSource.setOracleDataSource(oracleDataSource());
-
return commonDataSource;
-
}