@Bean和@Configuration
@Configuration類似于@Component,它标明目前類時一個配置類,用于配置bean。
@Bean對應xml配置中的<bean/>标簽,擁有<bean/>标簽所有的屬性,如果init-method等屬性
@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyServiceOne();
}
}
AppConfig類等價于如下配置
<beans>
<bean id="myService" class="com.acme.services.MyServiceOne"/>
</beans>
MyServiceOne是我們上一節中定義的MyService接口的一個實作類
AnnotationConfigApplicationContext
此節之前我們都是使用的ClassPathXmlApplicationContext讀取的xml配置檔案進行的容器初始化。即便我們使用了基于注解的配置,也需要配置xml标簽自動掃描包等。有了AnnotationConfigApplicationContext之後,我們就可以不再使用xml配置了。
package com.yyoo.boot.config;
import com.yyoo.boot.annotation.MyService;
import com.yyoo.boot.annotation.MyServiceImplOne;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppCofig1 {
@Bean
public MyService getMyService(){
return new MyServiceImplOne();
}
}
import com.yyoo.boot.config.AppCofig1;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Demo9 {
@Test
public void test(){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppCofig1.class);
MyService myService = context.getBean(MyService.class);
System.out.println(myService);
myService.print();
}
}
使用AnnotationConfigApplicationContext的register方法
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();
}
使用自動元件掃描
package com.yyoo.boot.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan({"com.yyoo.boot"})
public class AppConfig2 {
}
我們在AppConfig2中沒有定義Bean,通過scan掃描了com.yyoo.boot包下的所有bean,我們之前章節的代碼中有MyService以及它的兩個實作類都使用了@Service注解标注,在scan掃描的時候會将他們掃描到容器,容器可以直接使用。
package com.yyoo.boot.annotation;
import com.yyoo.boot.config.AppConfig2;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Demo10 {
@Test
public void test(){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig2.class);
MyService myService = (MyService)context.getBean("myServiceImplOne");
System.out.println(myService);
myService.print();
}
}
我們的示例getBean的時候直接通過bean的名稱擷取了MyServiceOne的執行個體,如果我們直接通過type擷取的話MyService有兩個滿足條件的實作類,容器會報錯。因為我們沒有使用@Primary等注解。
如果我們不用@ComponentScan注解,也可以通過AnnotationConfigApplicationContext的scan方法定義掃描包
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.scan("com.yyoo.boot");
ctx.refresh();
MyService myService = ctx.getBean(MyService.class);
}
實際一般建議使用@ComponentScan注解定義掃描包即可。
@ComponentScan排除某些類不掃描或包含某些類需要掃描
package com.yyoo.boot.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(value = {"com.yyoo.boot"},
includeFilters = @ComponentScan.Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
excludeFilters = @ComponentScan.Filter(Repository.class))
public class AppConfig2 {
}
其中FilterType枚舉類中包含如下幾類
過濾器類型 | 示例 | 說明 |
---|---|---|
ANNOTATION(預設類型) | @ComponentScan.Filter(Repository.class) | 任意的注釋類 |
ASSIGNABLE_TYPE | 任意類的class | 任意的類 |
ASPECTJ | org.example…*Service+ | 目标比對的AspectJ類型表達式 |
REGEX | org.example.Default.* | 比對的正規表達式 |
CUSTOM | org.example.MyTypeFilter | 自定義實作的org.springframework.core.type.TypeFilter接口 |
AnnotationConfigWebApplicationContext
在web應用中,我們使用AnnotationConfigWebApplicationContext來時效性上述功能。
<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>
以上是官網示例,如果使用spring boot此處可以跳過。
@Bean的工作機制
我們再用之前的A、B兩個類來做示例
package com.yyoo.boot.bean;
public class A {
public A(){
System.out.println("A無參構造");
}
}
package com.yyoo.boot.bean;
public class B {
private A a;
public B(){
System.out.println("B無參構造");
}
public B(A a){
this.a = a;
System.out.println("B帶參構造");
}
public A getA() {
return a;
}
public void setA(A a) {
this.a = a;
System.out.println("B的setA方法");
}
}
@Bean示例程式
package com.yyoo.boot.config;
import com.yyoo.boot.bean.A;
import com.yyoo.boot.bean.B;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig2 {
@Bean
public A getA(){
return new A();
}
@Bean
public B getB(){
return new B(getA());
}
@Bean
public B getB1(){
return new B(getA());
}
}
package com.yyoo.boot.annotation;
import com.yyoo.boot.bean.B;
import com.yyoo.boot.config.AppConfig2;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Demo10 {
@Test
public void test(){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig2.class);
B b = (B)context.getBean("getB");
B b1 = (B)context.getBean("getB1");
System.out.println(b.getA());
System.out.println(b1.getA());
}
}
執行結果
14:53:05.809 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Refreshing org.spring[email protected]7a0ac6e3
14:53:05.899 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
14:53:06.243 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
14:53:06.248 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
14:53:06.252 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
14:53:06.255 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
14:53:06.275 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'appConfig2'
14:53:06.288 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'getA'
A無參構造
14:53:06.312 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'getB'
B帶參構造
14:53:06.313 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'getB1'
B帶參構造
[email protected]
[email protected]
1、最直覺的結果,A的無參構造隻執行了一次。雖然我們在getB或getB1方法中都調用了方法getA,而且getA也是@Bean注解的方法,但是getA隻執行了一次。因為getA也是@Bean注解的,在執行個體化後會被緩存起來,在getB執行的時候會先查詢緩存的getA。而且兩個B類的示例中a屬性是一個執行個體。
2、我們的示例中@Bean沒有定義name屬性,那麼其定義的bean預設名稱是對應的方法名稱(注意跟@Component的預設名稱差別),也就是我們的示例中定義的bean名稱分别為:getA、getB、getB1
所有@Configuration類在啟動時都使用CGLIB. 在子類中,子方法在調用父方法并建立新執行個體之前,首先檢查容器中是否有任何緩存的(作用域)bean。從 Spring 3.2 開始,CGLIB 類已被重新打包org.springframework.cglib并直接包含在 spring-core JAR 中。是以我們不再需要單獨引入CGLIB的包。
讓我們把getA方法的@Bean注解去掉試試
14:52:07.176 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Refreshing org.spring[email protected]7a0ac6e3
14:52:07.228 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
14:52:07.474 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
14:52:07.478 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
14:52:07.481 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
14:52:07.484 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
14:52:07.498 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'appConfig2'
14:52:07.507 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'getB'
A無參構造
B帶參構造
14:52:07.527 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'getB1'
A無參構造
B帶參構造
[email protected]
[email protected]
A的無參構造調用了兩次。因為我們沒有@Bean注解getA方法,那麼在getB和getB1方法中調用該方法就成了普通的方法調用,是以A被調用了兩次而且對應的getB和getB1的a屬性不是同一個執行個體。
以上示例的另一種寫法
package com.yyoo.boot.config;
import com.yyoo.boot.bean.A;
import com.yyoo.boot.bean.B;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig2 {
@Bean
public A getA(){
return new A();
}
@Bean
public B getB(A a){
return new B(a);
}
@Bean
public B getB1(A a){
return new B(a);
}
}
這樣Spring會為兩個getB方法注入getA執行個體
如果我們有多個A執行個體怎麼辦?
package com.yyoo.boot.config;
import com.yyoo.boot.bean.A;
import com.yyoo.boot.bean.B;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig2 {
@Bean
public A getA(){
return new A();
}
@Bean
public A getA1(){
return new A();
}
@Bean
public B getB(A a){
return new B(a);
}
@Bean
public B getB1(A a){
return new B(a);
}
}
以上寫法會報錯,因為在getB方法上對應的A參數執行個體,Spring在注入時不知道該注入getA還是getA1,此時我們需要借助@Qualifier注解
package com.yyoo.boot.config;
import com.yyoo.boot.bean.A;
import com.yyoo.boot.bean.B;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig2 {
@Bean
public A getA(){
return new A();
}
@Bean
public A getA1(){
return new A();
}
@Bean
public B getB(@Qualifier("getA")A a){
return new B(a);
}
@Bean
public B getB1(@Qualifier("getA1")A a){
return new B(a);
}
}
如果我們的getA是prototype而不是單例的會是什麼結果
package com.yyoo.boot.config;
import com.yyoo.boot.bean.A;
import com.yyoo.boot.bean.B;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
@Configuration
public class AppConfig2 {
@Bean
@Scope("prototype")
public A getA(){
return new A();
}
@Bean
public B getB(A a){
return new B(a);
}
@Bean
public B getB1(A a){
return new B(a);
}
}
package com.yyoo.boot.annotation;
import com.yyoo.boot.bean.B;
import com.yyoo.boot.config.AppConfig2;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Demo10 {
@Test
public void test(){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig2.class);
B b = (B)context.getBean("getB");
B b1 = (B)context.getBean("getB1");
System.out.println(b.getA());
System.out.println(b1.getA());
}
}
結果如下
15:17:57.051 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Refreshing org.spring[email protected]7a0ac6e3
15:17:57.092 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
15:17:57.364 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
15:17:57.371 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
15:17:57.374 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
15:17:57.377 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
15:17:57.394 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'appConfig2'
15:17:57.406 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'getB'
A無參構造
15:17:57.438 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Autowiring by type from bean name 'getB' via factory method to bean named 'getA'
B帶參構造
15:17:57.441 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'getB1'
A無參構造
15:17:57.441 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Autowiring by type from bean name 'getB1' via factory method to bean named 'getA'
B帶參構造
[email protected]
[email protected]
上一篇:006-Spring IoC 基于注解配置
下一篇:008-Spring Ioc 環境與配置