從Spring3.0,@Configuration用於定義配置類,可替換xml配置文件,被註解的類內部包含有一個或多個被@Bean註解的方法,這些方法將會被ConfigurationClassPostProcessor類進行掃描,並用於構建bean定義,初始化Spring容器。
我們先來看一下@Configuration註解的源碼:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
@AliasFor(annotation = Component.class)
String value() default "";
}
可以看到@Configuration內部又有一個@Component註解,所以,其作用和@Component註解一樣,都能將一個標有@Configuration的註解類,通過Spring包掃描和解析,最終形成Spring的bean。不過Spring在內部對於加了@Configuration註解的配和加了@Component註解的類,處理起來是由差別的,如果有同學想要探究Spring對於@Configuration註解的類和其他@Component註解的類的不同,可以查看:Spring中@Configuration源碼深度解析(一)和Spring中@Configuration源碼深度解析(二),深入學習@Configuration註解。
Spring從3.0開始引入@Configuration是爲了替代XML形式的配置文件,當你在做ssm項目的時候,使用的應該是xml格式的配置文件,如:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd default-lazy-init="false">
<context:component-scan base-package="com.luban.factory" />
<bean id="school" class="com.luban.factory.School" >
<property name = "student" ref ="student"></property>
</bean>
<bean id="student" class="com.luban.factory.Student"/>
</beans>
使用@Configuration來替換xml配置類如下:
@Configuration
@ComponentScan("com.luban.factory") //對應xml裏面的context:component-scan標籤
public class Config {
@Bean //對應xml裏面的Bean標籤
public School school(){
School school = new School();
//給school對象設置屬性
school.setStudent(student());
return school;
}
@Bean
public Student student(){
return new Student();
}
}
他們所完成的事情是相同的,在方法上使用@Bean就相當於xml裏面的bean標籤,@ComponentScan用來指定需要掃描的包的全路徑,會把路徑下在類上面標註了@Component、@Service、@Configuration等注入到容器中。
由於是作爲配置類來使用,有的人該說了,既然@Configuration底層就是使用@Component註解,那麼能不能不用了@Configuration了,直接用@Component註解,那下面我們來實驗一下,把@Configuration換成@Component試一下:
@Component //本次使用的是@Component
@ComponentScan("com.luban.factory")
public class Config {
@Bean
public School school(){
School school = new School();
//給school對象設置屬性
school.setStudent(student());
return school;
}
@Bean
public Student student(){
return new Student();
}
}
public class Test01 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
System.out.println(context.getBean("school"));
System.out.println(context.getBean("student"));
}
}
//打印結果:
//com.luban.factory.School@41a4555e
//com.luban.factory.Student@3830f1c0
//com.luban.factory.Student@39ed3c8d
並且該配置類能夠注入到容器裏面,並且也可以完成對@Bean的解析,並把@Bean標註的方法變成Bean注入到容器中。
我們再將@Component換成@Configuration試一下:
@Configuration//本次使用的是@Configuration
@ComponentScan("com.luban.factory")
public class Config {
@Bean
public School school(){
School school = new School();
//給school對象設置屬性
school.setStudent(student());
return school;
}
@Bean
public Student student(){
return new Student();
}
}
public class Test01 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
System.out.println(context.getBean("school"));
System.out.println(context.getBean("student"));
}
}
//其打印結果
//com.luban.factory.School@6debcae2
//com.luban.factory.Student@5ba23b66
//com.luban.factory.Student@5ba23b66
發現@Component和@Configuration沒什麼區別啊,都能完成對Spring中Bean的管理,不過呢,你仔細看打印結果,多看兩遍後你會發現,使用@Configuration註解之後,student對象是同一個,而使用@Component註解的話,student對象不是同一個,爲什麼加了@Configuration註解之後,student對象就是同一個呢,就只是改了一個註解而已,這是你可以知道了,肯定是加了@Configuration註解之後,Spring對其做了不同的處理,其實就是這樣的,能夠完成這樣的工作,肯定是使用了代理,又因爲這個配置類沒有接口,所以可以斷定,加了@Configuration之後,會使用cglib對其進行增強,然後進行方法攔截。如果想深入瞭解Spring對@Configuration的處理過程,請查看Spring中@Configuration源碼深度解析(一)和Spring中@Configuration源碼深度解析(二),深入學習@Configuration註解。
關於@Configuration有了一定的瞭解,就要看一下@Bean註解的使用,基本上@Configuration註解類裏面都是定義@Bean註解的方法,以這樣的方式往容器中注入bean,
我們來查看一下@Bean的源碼:
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {
//指定bean的名稱
@AliasFor("name")
String[] value() default {};
@AliasFor("value")
String[] name() default {};
//屬性的注入模型
Autowire autowire() default Autowire.NO;
//指定init方法
String initMethod() default "";
//指定destroy方法
String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;
}
所以使用@Bean註解可以寫成這樣的格式:
@Bean(name = "sch",initMethod = "init" ,destroyMethod = "destroy")
public School school(){
School school = new School();
//給school對象設置屬性
school.setStudent(student());
return school;
}
public class School {
private Student student;
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
public void init(){
System.out.println("執行init方法");
}
public void destroy(){
System.out.println("執行destroy方法");
}
}
public class Test01 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
School school = (School) context.getBean("sch"); //此時取的是sch
System.out.println(school);
context.destroy();
}
}
//其打印結果:
//執行init方法
//com.luban.factory.School@41a4555e
//執行destroy方法
注意這裏指定initMethod和destroyMethod的方法,需要是School對象的方法,不能是其他對象的。
關於@Configuration的總結:
1.@Configuration標註的類會被cglib代理,從而進行增強
2.@Configuration標註的類不能使用final關鍵字修飾(cglib是使用繼承增強需要代理的類的)
3.@Configuration標註的類在@Bean方法內部調用其他標有@Bean的方法時,會被攔截,然後直接直接從容器中進行獲取,不用再次創建(就因爲有這一點,所以配置類要是有@Configuration,使用其他的可能會帶來不必要的錯誤)
4.@Configuration標註的類會被標註爲full類型的,用來區分@Component、@Service等註解,增強的時候就是根據是不是full進行的。