目錄
一、配置spring容器(將bean裝配到spring容器中)
4.1 Singleton Beans with Prototype-bean Dependencies
4.1.1 ApplicationContextAware接口
5.1 Initialization Callbacks(初始化回調)
5.2 Destruction Callbacks(銷燬回調)
5.3 Default Initialization and Destroy Methods(默認初始化和銷燬方法)
5.3.1 XML(init-method和destroy-method)
一、配置spring容器(將bean裝配到spring容器中)
使用ioc首先就要配置好spring容器,要告訴容器我需要它管理的bean是什麼,以及bean之間的依賴關係
配置spring(初始化spring環境)的方式有三種(spring的編程風格):
- schemal-based-------xml
- annotation-based-----annotation
- java-based----java Configuration
我們首先創建好項目
IndexDao接口:
package priv.cy.dao;
public interface IndexDao {
public void test();
}
IndexDao實現類:
package priv.cy.dao;
public class IndexDaoImpl implements IndexDao {
private String str;
@Override
public void test() {
System.out.println(str);
System.out.println("Impl");
}
public void setStr(String str) {
this.str = str;
}
public String getStr() {
return str;
}
}
Service類:
package priv.cy.dao;
public class IndexService {
private IndexDao dao;
public IndexService(IndexDao dao) {
this.dao = dao;
}
public void service() {
dao.test();
}
}
1.1、XML方式
首先講第一種,使用XML
先在resources創建一個XML,XML保存到resource包中,XML文件的名字可以任意,這裏命名爲spring.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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--上面這些叫做命名空間-->
<!--在bean標籤中指定要裝配給spring容器的類,在class屬性寫好類的全限定名,設置其id,如果不設置name默認name和id相同-->
<bean id="dao" class="priv.cy.dao.IndexDaoImpl">
<property name="str" value="aaa"></property>
</bean>
<bean id="service" class="priv.cy.dao.IndexService">
<!--使用構造方法注入-->
<constructor-arg ref="dao"></constructor-arg>
<!--使用set方法注入,這裏配置的是service這個類依賴的對象,ref中寫的是xml文件中定義的該對象的id-->
<property name="dao" ref="dao"></property>
</bean>
</beans>
在這個XML文件中來指定要委託給spring容器管理的對象,然後指定對象之間的依賴關係,依賴關係是通過注入的方式來實現的,這裏再講一下注入方式,一共有三種
- 構造方法注入。
- setter方法注入。
- 接口注入
接口注入在spring3提供的,現在的spring版本已經取消了
上面展示了setter方法注入(使用<property>標籤)和構造方法注入(使用<constructor-arg>標籤),這兩種選一種使用就可以。注意,如果依賴的是已經交給spring容器管理的bean(即已經在xml中配置過的bean),依賴關係用ref,等號右邊寫定義的name。配置bean的時候如果只定義了id,那麼它的name默認和id一樣。
如果注入的不是被spring容器管理的bean,就不要用ref,比如indexDaoImpl依賴一個String對象,就用value後面直接寫String的值就可以了。
通過上面的配置,spring容器已經接管了我們的bean
創建測試類進行測試,注意創建spring容器對象時構造方法傳入的是classpath:spring.xml。
package priv.cy.dao;
public class Test {
public static void main(String[] args) {
/**
* XML是放到resource包下的,這樣編譯後纔會被複制到classpath路徑下,XML的編譯就是原樣複製,.java的編譯時編程.class再放到classpath目錄中
*/
// 如果使用了XML,就需要用這個類來獲取Spring容器,通過該對象來獲取bean
ClassPathXmlApplicationContext classPathXmlApplicationContext
= new ClassPathXmlApplicationContext("classpath:spring.xml");
IndexService service = (IndexService) classPathXmlApplicationContext.getBean("service");
service.service();
}
}
通過spring容器獲取bean,實現了IOC控制反轉
1.2、註解方式
再講第二種。使用註解的方式來配置spring容器
首先註解的方式依舊還是需要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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--上面這些網址叫做命名空間-->
<!--剛剛使用的是XML的方式來管理spring容器,下面講解用註解的方式管理容器-->
<!--還可以使用註解來管理spring容器,但是spring默認是關閉註解支持的,並且不會去掃描註解,這麼做是爲了節約性能-->
<!--開啓支持註解解析-->
<context:annotation-config></context:annotation-config>
<!--開啓掃描com包下面的所有註解-->
<context:component-scan base-package="priv"></context:component-scan>
<!--以前spring要使用註解管理spring容器這兩條必須要寫,但是現在把第一條合併到第二條了,所以現在寫下面這一條就可以了-->
</beans>
Dao類:
通過註解,將IndexDaoImpl裝配到Spring容器中去,可以通過@Value標籤給依賴的String對象注入常量值
package priv.cy.dao;
import org.springframework.stereotype.Component;
// 這裏可以指定放入容器之後這個類的id是dao,如果不寫的話默認就是類名
@Component("dao")
public class IndexDaoImpl implements IndexDao {
@Value("aaa")
private String str;
@Override
public void test() {
System.out.println(str);
System.out.println("Impl 0");
}
public void setStr(String str) {
this.str = str;
}
public String getStr() {
return str;
}
}
Service類:
通過註解將該類裝配到Spring容器中,並且直接通過@Autuwired註解將依賴的對象注入給IndexDaoImpl
package priv.cy.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
// 可以在括號裏指定他的id,如果不指定id默認就是類名
@Service
public class IndexService {
@Autowired // 默認使用的是構造方法進行注入
private IndexDao dao;
public IndexService(IndexDao dao) {
this.dao = dao;
}
public void service() {
dao.test();
}
}
測試類:
也就是使用解析XML的那個類來獲取spring容器對象,進而來獲取bean
package priv.cy.dao;
public class Test {
public static void main(String[] args) {
/**
* XML是放到resource包下的,這樣編譯後纔會被複制到classpath路徑下,XML的編譯就是原樣複製,.java的編譯時編程.class再放到classpath目錄中
*/
// 如果使用了XML,就需要用這個類來獲取Spring容器,通過該對象來獲取bean
ClassPathXmlApplicationContext classPathXmlApplicationContext
= new ClassPathXmlApplicationContext("classpath:spring.xml");
IndexService service = (IndexService) classPathXmlApplicationContext.getBean("service");
service.service();
}
}
AnnotationConfigApplicationContext和ClassPathXmlApplicationContext這兩個對象的getBean方法傳入的參數可以是bean的name,也可以是xxx.class,即可以通過name來獲取bean,也可以通過type來獲取bean。如果傳入的是xx.class,那麼返回的對象就是這個類型,不需要強轉,如果傳入的是name,那麼返回的類型就是Object,需要強轉,除非在傳入的參數中添加一個class參數指名bean的類型。例如:
IndexDaodao = annotationConfigApplicationContext.getBean("indexDao", IndexDao.class);
1.3、JavaConfig
最後講一下javaConfig的當時來管理spring容器,因爲從上面兩種可以看出,都是需要有XML文件的,即使是註解方式,也需要有XML文件來開啓支持註解。但是javaConfig方法,就可以完全不需要XML文件。
package priv.cy.dao;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* 前面講的是XML和註解的管理spring的方式,可是註解的方式本身還需要通過XML來開啓註解支持,所以本質還是離不開XML
* 所以就可以使用Javaconfig的方式,直接創建一個Java類來管理spring,完全不需要XML
*/
// 將該類設置爲一個配置類,該類就相當於一個XML
@Configuration
// 開啓掃描,指定要掃描哪些包,就和XML中開啓掃描的作用是一樣的,如果不開啓就沒有辦法使用註解。必須要指定掃描位置
@ComponentScan("priv")
public class Spring {
}
通過這個JavaConfig類,也就開啓了註解支持,後面的描述bean之間的依賴關係就可以直接使用註解了,完全不需要XML
在獲取spring容器對象的時候會有些不同,因爲spring解析XML和解析javaConfig功能的實現是使用的不同的類,所以通過javaConfig配置spring容器的話,獲取spring容器對象的時候需要使用另一個對象,注意傳入的是spring.class。
AnnotationConfigApplicationContext(通過註解的方式初始化應用程序上下文)這個類本身是支持解析註解的功能的,之前的ClassPathXmlApplicationContext(通過XML的方式初始化應用程序上下文)不支持解析註解,所以在之前如果要使用註解,還需要在XML中開啓支持解析註解。但是AnnotationConfigApplicationContext類本身就支持解析註解,所以也就不用再從XML中開啓了。只需要在javaConfig類中指定要掃描的位置就可以了。
測試類:
package priv.cy.dao;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
//如果沒有使用XML,使用的Javaconfig的方式來進行管理spring的,就不能使用上面的方法來獲取Spring容器了,要使用這個類來獲取
AnnotationConfigApplicationContext annotationConfigApplicationContext
= new AnnotationConfigApplicationContext(Spring.class);
IndexService service1 = (IndexService) annotationConfigApplicationContext.getBean("service");
service.service();
}
}
1.4、混合使用
也可以混合使用,即用Javaconfig也用XML,不用註解將indexDaoImpl裝載到spring容器了,也不用註解來標識各個bean之間的依賴關係,而是直接通過XML來用bean標籤進行裝載
然後在Javaconfig那個類上再加一個註解@ImportResource("classpath:spring.xml"),表示也根據xml的配置來管理spring容器,解析這個XML,進行混合使用
package priv.cy.dao;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("priv")
@ImportResource("classpath:spring.xml")
public class Spring {
}
我不是很喜歡寫XML,所以我一般就是使用javaConfig的方式和註解的方式來配置spring容器
1.5、實現bean的單獨註冊
所謂的初始化spring環境就是將我們交給spring管理的類實例化
這裏註冊的意思其實就是裝配
我們已經講了上面幾個初始化spring的環境的方法
1、xml ClassPathXmlApplicationContext 提供了類的掃描和單獨bean的註冊(就是在xml定義一個類的bean標籤,spring容器就會完成這個類的聲明和註冊,也就是實例化)這兩個功能,xml可以不用掃描代碼就可以完成對指定bean的裝配,只要是在xml聲明瞭,它就會對這個類完成註冊,實現實例化。
2、annotation 使用註解
3、javaConfig AnnotationConfigAoolicationContext 類的掃描 類的定義(Java代碼) 不能單獨註冊一個類,就是說必須進行掃描,纔可以實現類的定義,類的掃描和類的定義是不能分開的。從代碼層面就是說如果我們不開啓掃描註解,我們添加的註解就是無效的。
但實際上spring給Annotation提供了單獨註冊方法,只不過用的很少,可以不使用掃描直接去將指定的類在spring容器中註冊,完成bean的裝配。我們可以使用register這個方法來完成對bean的單獨註冊
先說一下register這個方法:
javaConfig:
@Configuration
@ComponentScan("priv")
public class AppConfig {
}
service:
@Service
public class IndexService {
public void service() {
System.out.println("service");
}
}
測試類:
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext
= new AnnotationConfigApplicationContext();
// 實現類的單獨註冊,不需要掃描註解就可以將其來spring容器中實例化
annotationConfigApplicationContext.register(AppConfig.class);
// 使用register方法來實現類的單獨註冊必須要refresh
annotationConfigApplicationContext.refresh();
System.out.println(annotationConfigApplicationContext.getBean(IndexService.class).getClass().getName());
}
}
執行結果:
我們發現直接將配置JavaConfig配置文件直接註冊進register方法也是可以實現相同的效果,掃描註解,實現裝配,但是使用register方法進行註冊必須要手動refreshsh。這些都可以從源碼中找到原因。
這個是AnnotationConfigApplicationContext類的構造方法源碼,也就是我們平時將javaConfig的class傳入進行spring容器配置的方法。
public AnnotationConfigApplicationContext(Class<?>...annotatedClasses) {
this();
register(annotatedClasses);
refresh();
}
我們發現原有的構造方法本質也是調用的register方法實現的配置文件註冊,並且幫我們refresh了,所以如果我們直接調用register方法,我們還需要自己手動refresh。
那麼如何使用register實現單獨bean註冊呢,很簡單
我們不傳入javaConfig類,並且將service類上的@Service註解去掉,看看如何在無註解和不進行掃描的情況下手動將我們的bean裝配到spring容器中
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext
= new AnnotationConfigApplicationContext();
// 實現類的單獨註冊,不需要掃描註解就可以將其來spring容器中實例化
annotationConfigApplicationContext.register(IndexService.class);
// 使用register方法來實現類的單獨註冊必須要refresh
annotationConfigApplicationContext.refresh();
System.out.println(annotationConfigApplicationContext.getBean(IndexService.class).getClass().getName());
}
}
執行結果:
很簡單,我們只需要將要裝配的類的class傳入到register方法中就實現了單獨註冊裝配,沒有添加javaConfig配置,不需要掃描,也不需要加註解。
如果閱讀了register源碼,我們就能發現register支持註冊兩種bean,一個是普通bean,一個就是javaConfig的bean,所以它不光支持讀取我們的配置類,也支持讓我們單獨註冊bean到spring容器當中
1.6 插一句:
XML中還有一個depends-on 這個可以規定bean在容器中的創建順序。
<bean id="beanOne" class="ExampleBean" depends-on="manager"/>
<bean id="manager" class="ManagerBean" />
比如上面的這種配置,通過depends-on配置,那麼manager就會先於beanOne創建,但是這兩個bean之間是沒有依賴關係的。這種操作的應用場景就是如果beanOne的初始化需要用到manager的一些信息或者beanOne中的一些靜態方法需要用到manager的信息,那麼就得保證manager先於beanOne創建。但是這兩個bean之間並沒有依賴關係,所以只能使用depends-on的方法
二、自動裝配
下面我們再說一下自動裝配這個問題
定義還是要定義的,就是說要將哪個類作爲bean裝配給spring容器管理,但是通過自動裝配的技術bean之間的依賴關係不需要我們來設置了,比如如果使用XML來配置,我們就不需和上面一樣還要在bean標籤中再嵌套設置該bean所依賴的對象了。
只需要把所有要裝配給spring容器的對象在XML文件中設置好或者用註解指定好,然後spring會直接根據定義類時的代碼獲取到依賴關係,自動給需要依賴的對象進行裝配,這就大大減少了描述依賴關係的配置。
手動裝配的優先級高於自動裝配。
自動裝配的優點:
- 大大減少我們描述依賴關係是的大篇幅配置
- 如果對象依賴關係更新,也不需要重新配置
自動裝配的方法:
- no/default
- byName
- byType
- constructor
指定自動裝配的方式有兩種:
- 全局配置:全局都用指定的自動裝配方法,如全局都用byName
- 局部配置:指定的自動裝配方法只在局部有效
2.1、使用XML來完成自動裝配
全局指定,直接在XML頭部設置自動裝配方式
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="byType">
<bean id="dao" class="priv.cy.dao.IndexDaoImpl"></bean>
<bean id="service" class="priv.cy.dao.IndexService"></bean>
</beans>
2.1.1、byType
就是通過類型來查找相應的依賴關係
一上面的XML配置文件爲例
開啓了自動裝配之後,首先spring先掃描XML文件中設置的要裝配給spring容器管理的bean,然後掃描到了indexService這個bean之後,會先去掃描定義這個類的代碼,發現裏面有一個類型是IndexDao的依賴對象,然後就會返回來掃描XML配置文件,查看有沒有交給spring容器管理的這個類型的bean,最後發現了id爲dao的這個bean,然後就直接將這個bean注入給indexService對象了,這就是通過類型來進行自動裝配。
XML中配置的bean,底層都會講他們添加到一個map中,所以上面掃描bean的過程在底層就是遍歷這個map。
但是上述方法有一個問題,剛纔的例子只是將一種IndexDao接口的實現類裝配到spring容器中去管理,如果將兩種IndexDao接口的實現類裝配到spring容器中,就會出現報錯,因爲spring容器發現這兩種類型都可以注入給IndexService,不知道應該使用哪個了
例如定義兩個IndexDao實現類:
package priv.cy.dao;
public class IndexDaoImpl implements IndexDao {
@Override
public void test() {
System.out.println("Impl 0");
}
}
package priv.cy.dao;
public class IndexDaoImpl1 implements IndexDao {
@Override
public void test() {
System.out.println("Impl 1");
}
}
將其交給spring容器管理:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="byType">
<bean id="dao" class="priv.cy.dao.IndexDaoImpl"></bean>
<bean id="dao1" class="priv.cy.dao.IndexDaoImpl1"></bean>
<bean id="service" class="priv.cy.dao.IndexService"></bean>
</beans>
運行時報錯如下:
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'priv.cy.dao.IndexDao' available: expected single matching bean but found 2: dao,dao1
上面的意思就是說應該注入IndexDao類型的對象,但是發現了兩個bean,dao和dao1都可以注入,這纔會報錯。這時就需要用byName來解決
而且還有一點要注意,使用自動裝配的時候默認使用的是無參構造方法來創建對象,所以要確保所有的bean都有無參構造方法,要麼不顯示地寫構造方法,要麼顯示寫出無參構造方法(如果寫了有參構造方法必須也寫上無參構造方法,因爲有參構造方法會使默認的隱式無參構造方法失效)
2.1.2、byName
這裏就是通過指定bean的Name來進行自動裝配
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="byName">
<bean id="dao" class="priv.cy.dao.IndexDaoImpl"></bean>
<bean id="dao1" class="priv.cy.dao.IndexDaoImpl1"></bean>
<bean id="service" class="priv.cy.dao.IndexService"></bean>
</beans>
前面說過,如果不指定bean的name,那麼name默認和id一樣,使用byName進行自動裝配的時候,使用的是類的set方法名來進行Name匹配的,如:
package priv.cy.dao;
public class IndexService {
private IndexDao dao;
public void service() {
dao.test();
}
public void setDao(IndexDao dao) {
this.dao = dao;
}
}
比如上面的service類,setter方法的方法名是setDao,那麼再進行自動裝配的時候就會自動將方法名前面的set去掉,將剩下的第一個字母變小寫,得到的name去和XML中設置的bean的name進行比較,找到name爲dao的這個bean,將其注入到service類中。此時運行service方法,就會輸出Impl 0
所以這裏要識別的名字只和set方法中的哪個後綴名字有關,和service類中定義的依賴對象的名字 private IndexDao dao;,也就是dao是沒有關係的。
比如我將
<bean id="dao1" name="aaa" class="priv.cy.dao.IndexDaoImpl1"></bean>
IndexDaoImpl1這個類的name改成aaa,然後將service中的setter改爲setAaa
public class IndexService {
private IndexDao dao;
public void service() {
dao.test();
}
public void setAaa(IndexDao dao) {
this.dao = dao;
}
}
這個時候輸出的就是Impl 1而不是Impl 0l了,說明byName只和bean的name值以及setter方法的名字有關係。和定義類的時候給依賴對象起的名字無關。
2.1.3、no和default
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="default">
<bean id="dao" class="priv.cy.dao.IndexDaoImpl"></bean>
<bean id="dao1" class="priv.cy.dao.IndexDaoImpl1"></bean>
<bean id="service" class="priv.cy.dao.IndexService"></bean>
</beans>
這兩個作用一樣,都是使用手動裝配,如果沒有手動裝配的話,會報空指針錯誤
2.1.4、constructor
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="constructor">
<bean id="dao" class="priv.cy.dao.IndexDaoImpl"></bean>
<bean id="dao1" class="priv.cy.dao.IndexDaoImpl1"></bean>
<bean id="service" class="priv.cy.dao.IndexService"></bean>
</beans>
這個和byType差不多,都是依靠類型來進行自動裝配,區別就是byType根據的是定義的成員屬性的類型來進行自動裝配的
public class IndexService {
private IndexDao dao;
public void service() {
dao.test();
}
public void setDao(IndexDao dao) {
this.dao = dao;
}
}
它是根據裏面private IndexDao dao; 這裏面定義的依賴對象的類型IndexDao來spring容器中的bean進行類型匹配的
而constructor使用的是構造方法的類型來進行匹配的,所以使用這種方式要設置好構造方法
public class IndexService {
private IndexDao dao;
public IndexService (IndexDao dao) {
this.dao = dao;
}
public void service() {
dao.test();
}
public void setDao(IndexDao dao) {
this.dao = dao;
}
}
他就會根據構造方法中傳入的參數類型進行匹配,取得了構造方法中傳入的參數類型是IndexDao,然後再去spring容器中找這個類型的bean,找到後將其注入到IndexService的bean中。
注意使用這種方法如果沒有設置好構造方法,就會發生錯誤。
上面說的都是全局,自動裝配也可以局部配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
>
<bean id="dao" class="priv.cy.dao.IndexDaoImpl"></bean>
<bean id="dao1" class="priv.cy.dao.IndexDaoImpl1"></bean>
<bean id="service" class="priv.cy.dao.IndexService" autowire="byName"></bean>
</beans>
像這種,在需要自動裝配的bean標籤中,添加autowire屬性,等號右邊是自動裝配的方式。這樣只有service這個bean使用自動裝配,其他的bean都是用手動裝配。
但是局部配置的最廣泛的用處還是與全局配置混合使用,局部配置的優先級要高於全局配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="byType">
<bean id="dao" class="priv.cy.dao.IndexDaoImpl"></bean>
<bean id="dao1" class="priv.cy.dao.IndexDaoImpl1"></bean>
<bean id="service" class="priv.cy.dao.IndexService" autowire="byName"></bean>
</beans>
向這種寫法就可以實現service這個bean使用byName進行自動裝配,其他的bean使用byType進行自動裝配。這樣就可以防止service的bean使用byType時發現有多個相同類型的bean都可以注入到service中,出現衝突報錯了
2.2、使用JavaConfig和註解來進行自動裝配
下面我們使用JavaConfig和註解來進行自動裝配
這裏就需要說一下使用註解的時候,bean的命名規則:
- 在使用@Component、@Repository、@Service、@Controller等註解創建bean時,如果指定bean的name,則是指定的名稱.
- 如果不指定bean的name,bean名稱的默認規則是類名的首字母小寫,如SysConfig - sysConfig,Tools - tools。
- 如果類名前兩個或以上個字母都是大寫,那麼bean名稱與類名一樣,如RBACUserLog - RBACUserLog,RBACUser - RBACUser,RBACRole - RBACRole。
這裏和使用XML時的命名規則有些不同,使用XML來配置spring容器的時候bean標籤寫上id,然後如果不指定name的話,那麼bean的name默認和id一樣。
這裏插一條,spring中bean的命名規則是通過BeanNameGenerator這個接口來實現的,我們可以通過實現這個接口來完成自己的命名規則,編寫好命名規則類,然後告訴在告訴spring要掃描位置的同時,指定命名規則類就可以了。配置方法如下:
MyNameGenerator是我們自己編寫的命名規則類
javaConfig:
@Configuration
@ComponentScan(basePackages = "org.example", nameGenerator = MyNameGenerator.class)
public class AppConfig {
// ...
}
XML:
<beans>
<context:component-scan base-package="org.example"
name-generator="org.example.MyNameGenerator" />
</beans>
javaConfig類:
package priv.cy.dao;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("priv")
public class Spring {
}
service:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class IndexService {
@Autowired
private IndexDao indexDaoImpl;
public void service() {
indexDaoImpl.test();
}
public void setDao(IndexDao dao) {
this.indexDaoImpl = dao;
}
}
dao:
import org.springframework.stereotype.Repository;
@Repository
public class IndexDaoImpl implements IndexDao {
@Override
public void test() {
System.out.println("Impl 0");
}
}
import org.springframework.stereotype.Repository;
@Repository
public class IndexDaoImpl1 implements IndexDao {
@Override
public void test() {
System.out.println("Impl 1");
}
}
上面這些代碼通過註解將類交給spring容器去管理,默認的bean name就是他們的首字母小寫的類名
先講一下@Autowired和@Resource的區別
- @Autowired默認先使用的是byType,byType使用的是屬性的類型,如果byType會出現報錯的話,再就會再使用byName,這裏使用的name也是屬性的屬性名,用屬性的屬性名來去spring容器中匹配查找相同的bean name
- @Resource默認是使用的是byName,但是這裏的byName和之前講XML自動裝配的byName不太一樣,之前的byName都是通過setter方法名來匹配name的,但是@Resource使用的是屬性名來匹配name的,和setter方法沒有關係。@Resource可以指定使用byName還是byType。
以上兩種自動裝配的註解都不需要使用setter方法
2.2.1 @Autowired
例子:
@Service
public class IndexService {
@Autowired
private IndexDao indexDaoImpl;
public void service() {
indexDaoImpl.test();
}
public void setDao(IndexDao dao) {
this.indexDaoImpl = dao;
}
}
按道理說如果使用byType會出現報錯,因爲有兩個IndexDao接口的實現類,但是它不會直接報錯,@Autowired會再換成byName再試一次,這個時候的name使用的是屬性名的name,也就是用這個indexDaoImpl去查找,如果byName也沒有找到,就會報之前那個有兩個bean的衝突錯誤了。
所以註解(@Autowired和@Resource)使用byName都是直接使用依賴的對象的屬性名來進行查找的,和setter方法沒有任何關係,比如上面的這個代碼在spring容器中代理的IndexDaoImpl.class這個bean默認的name時indexDaoImpl,上面的代碼中setter方法名時setDao,按之前的道理IndexService取到的依賴類的名字應該是dao纔對,但是這裏依舊能將bean注入,但是此時spring容器中IndexDaoImpl這個類的bean的name是indexDaoImpl(默認命名規則)。說明此時IndexService是用indexDaoImpl這個name來從spring容器中進行查找匹配的(也就是使用的private IndexDao indexDaoImpl 裏面這個對象的名字來進行匹配的,如果此時吧這個對象的名字改成aaa,也就會報錯出現問題了)
2.2.2 @Resource
使用@Resource的話如果不指定的話,就默認是byName,如果是默認byName,那麼過程和上面的@Autowired一樣,但是@Resource可以指定使用byName還是byType
指定byType:
@Service
public class IndexService {
@Resource(type = IndexDaoImpl1.class)
private IndexDao dao;
public void service() {
dao.test();
}
public void setDao(IndexDao dao) {
this.dao = dao;
}
}
直接指定使用byType,並且使用IndexDaoImpl1.class這個類,這樣不管怎麼樣service這個類注入的已經是IndexDaoImpl1這個實現類
也可以指定byName
@Service
public class IndexService {
@Resource(name = "indexDaoImpl1")
private IndexDao dao;
public void service() {
dao.test();
}
public void setDao(IndexDao dao) {
this.dao = dao;
}
}
這樣就通過bean的name來進行查找,查找spring容器中bean的name是indexDaoImpl1的這個bean,將其注入給IndexService對象
三、spring懶加載
bean在調用的時候纔會被加載
使用懶加載的前提是這個bean的作用域必須是單例的。
3.1 使用XML設置:
用lazy-init。告訴spring容器是否以懶加載的方式創造對象。用的時候才加載構造,不用的時候不加載
取值:true(懶,真正調用到的時候再加載)、false(非懶,已啓動spring容器就創建對象)、default(懶)
<bean id="dao" class="cn.java.ioc.MW" lazy-init="default" ></bean> |
3.2 使用註解設置:
@Lazy
@Bean("dao")
public Dao dao() {
System.out.println("dao");
return new Dao();
}
3.3 懶加載與非懶加載的優缺點:
- 懶加載:對象使用的時候纔去創建,節省資源,但是不利於提前發現錯誤。
- 非懶加載:容器啓動的時候立刻創建對象。消耗資源。利於提前發現錯誤。
當scope=“prototype” (多例)時,默認以懶加載的方式產生對象。
當scope=“singleton” (單例)時,默認以非懶加載的方式產生對象。
四、spring作用域
spring容器中的bean可以分爲五個範圍。所有範圍的名稱都是說明的,
1.singleton:這種bean範圍是默認的,這種範圍確保不管接受到多個請求,每個容器中有一個bean的實例,單例模式由bean factory自身來維護。
2.prototype:原先通過範圍與單例範圍相反,爲每一個bean請求提供一個實例。
3.request:在請求bean範圍內會爲每一個來自客戶端的網絡請求創建一個實例,在請求完成之後,bean會失效並被垃圾回收器回收。該屬性僅對HTT請求產生作用,使用該屬性定義Bean時,每次HTTP請求都會創建一個新的Bean,適用於WebApplicationContext環境。
4.session:與請求範圍類似,確保每個session中的bean的實例在session過期後bean會隨之消失。該屬性僅用於HTTP Session,同一個Session共享一個Bean實例。不同Session使用不同的實例。Session
5.global-session:global-session和portlet公用全局存儲變量的話,那麼這全局變量需要存儲在global-session中。該屬性僅用於HTTP Session,同session作用域不同的是,所有的Session共享一個Bean實例。
下面重點討論singleton、prototyp作用域,request、session和global-session類作用域放到Spring MVC章節討論,這裏不再做詳細講述。
可以通過@Scope(" ")註解來設置
spring容器bean的作用域默認是單例,非懶加載的
4.1 Singleton Beans with Prototype-bean Dependencies
意思是在Singleton 當中引用了一個Prototype的bean的時候引發的問題,依賴的這個對象的原型作用域失效,也變成了單例了
其實這個問題就可以總結爲一個類依賴另一個類,但是這兩個的作用域是不同的,就會出現問題。
比如下面這一套代碼:
javaConfig:
@Configuration
@ComponentScan("priv")
public class Spring {
}
service:
@Service
@Scope("singleton")
public class IndexService {
@Autowired
private IndexDao dao;
public void service() {
System.out.println("service" + this.hashCode());
System.out.println("dao" + dao.hashCode());
}
public void setDao(IndexDao dao) {
this.dao = dao;
}
}
DAO:
@Repository
@Scope("prototype")
public class IndexDaoImpl implements IndexDao {
@Override
public void test() {
System.out.println("Impl 0");
}
}
Test:
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext
= new AnnotationConfigApplicationContext(Spring.class);
IndexService service = (IndexService) annotationConfigApplicationContext.getBean("indexService");
IndexService service1 = (IndexService) annotationConfigApplicationContext.getBean("indexService");
IndexService service2 = (IndexService) annotationConfigApplicationContext.getBean("indexService");
service.service();
service1.service();
service2.service();
}
}
結果:
可以發現service是單例的,而它依賴的dao是原型的,但是最終的結果確實dao的原型作用域失效,這就是作用域不同進行依賴錯產生的問題。因爲service是單例的,他在spring容器中只會創建一次,而它依賴的對象是隨着它的加載而加載的,所以service只加載一次,即使dao是原型的,也只會被加載一次。
我們的解決辦法只能是在service調用dao的時候想辦法重新獲取一次新的dao對象,這樣既能保證service對象是單例,也能保證每一次調用dao都是新產生的對象。
如果這個對象要依賴的類和自己的作用域不同,spring提供了兩種解決方法:
4.1.1 ApplicationContextAware接口
一種是讓這個對象實現ApplicationContextAware接口,這個還需要覆寫一個set方法,和引入ApplicationContext對象
這樣的解決辦法就是在service類每一次需要使用依賴的這個dao時,就通過ApplicationContext對象重新取一次這個bean,這樣dao就可以依舊保持原型的作用域,每一次使用它就會是一個新bean,而service對象一直保持單例不變。
javaConfig:
@Configuration
@ComponentScan("priv")
public class Spring {
}
service:
@Service
@Scope("singleton")
public class IndexService implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Autowired //加不加這個註解都無所謂了,反正最後使用的也不是這個dao對象 用ApplicationContext這個Autowired可加可不加
private IndexDao dao;
public void service() {
System.out.println("service" + this.hashCode());
//每次調用都使用applicationContext對象重新獲取IndexDaoImpl的bean
System.out.println("dao" + applicationContext.getBean("indexDaoImpl").hashCode());
}
public void setDao(IndexDao dao) {
this.dao = dao;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
DAO:
@Repository
@Scope("prototype")
public class IndexDaoImpl implements IndexDao {
@Override
public void test() {
System.out.println("Impl 0");
}
}
Test:
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext
= new AnnotationConfigApplicationContext(Spring.class);
IndexService service = (IndexService) annotationConfigApplicationContext.getBean("indexService");
IndexService service1 = (IndexService) annotationConfigApplicationContext.getBean("indexService");
IndexService service2 = (IndexService) annotationConfigApplicationContext.getBean("indexService");
service.service();
service1.service();
service2.service();
}
}
結果:
但是這種實現spring接口的方法,會大大增加項目的耦合度,侵入性太強,使代碼嚴重spring的API,spring的設計理念之一就是非侵入性,所以spring還提供了Lookup Method Injection這個方法來解決Singleton Beans with Prototype-bean Dependencies問題
4.1.2 Lookup Method Injection
使用@Lookup註解,就可以不需要依賴spring的API了
其他代碼和之前還是一樣,只需要修改service代碼:
@Service
@Scope("singleton")
public class IndexService {
// @Autowired 使用Lookup必須要將Autowired去掉,否則會出錯
private IndexDao dao;
public void service() {
System.out.println("service" + this.hashCode());
System.out.println("dao" + getDao().hashCode());
}
@Lookup
public IndexDao getDao() {
return null;
}
}
這裏只需要添加一個get方法,用來每一次調用時獲取全新的dao對象,方法名不重要,自定義就行,返回值就是你要獲取的對象類型,只需要在這個方法上面添加@Lookup註解,這個方法內部什麼邏輯代碼都不需要寫,直接寫一個return null就可以了。在每次獲取dao對象的位置調用這個方法就可以了。還有一點就是因爲是通過這個方法來獲取dao的bean了,所以也就不需要@Autowired這個註解進行注入了,要將其刪除,不刪除的話會出現報錯。
@Lookup是通過方法的返回值類型,根據這個類型從spring容器中查找這個bean,如果找到了就直接通過這個方法返回回來。但是這裏就有可能出現前面講的多個相同類型的bean衝突問題,如果IndexDao是一個接口,而這個接口有多個實現類被委託給spring容器進行管理,那麼這個時候如果還通過類型來查找就找到多個bean,進而出現錯誤
Exception in thread "main" org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'priv.cy.dao.IndexDao' available: expected single matching bean but found 2: indexDaoImpl,indexDaoImpl1
解決這個問題就還需要通過byName來進行查找,方法就是@Lookup("indexDaoImpl"),直接在這個註解指定bean的name,然後他就會通過這個name去spring容器中查找這個對應的bean,這樣就不會起衝突了
@Service
@Scope("singleton")
public class IndexService {
// @Autowired
private IndexDao dao;
public void service() {
System.out.println("service" + this.hashCode());
System.out.println("dao" + getDao().hashCode());
}
@Lookup("indexDaoImpl")
public IndexDao getDao() {
return null;
}
}
五、spring生命週期和回調
就相當於finally
生命週期回調有三種:
- Initialization Callbacks(初始化回調)
- Destruction Callbacks(銷燬回調)
- Default Initialization and Destroy Methods(默認初始化和銷燬方法)
5.1 Initialization Callbacks(初始化回調)
在初始化的時候回調,初始化是在實例化方法執行之後,需要實現InitializingBean接口,然後覆寫afterPropertiesSet()方法,這樣每次初始化的時候就會回調afterPropertiesSet方法
代碼:
@Repository
public class IndexDaoImpl1 implements IndexDao , InitializingBean {
public IndexDaoImpl1() {
System.out.println("Constructor");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("init");
}
}
測試類:
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext
= new AnnotationConfigApplicationContext(Appconfig.class);
}
}
執行結果:
5.2 Destruction Callbacks(銷燬回調)
當容器被銷燬的時候,會進行回調。需要實現DisposableBean這個接口,然後覆寫afterPropertiesSet()方法,這樣容器被銷燬的時候就會回調afterPropertiesSet方法
代碼:
@Repository
public class IndexDaoImpl1 implements IndexDao , InitializingBean , DisposableBean {
public IndexDaoImpl1() {
System.out.println("Constructor");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("init");
}
@Override
public void destroy() throws Exception {
System.out.println("destroy");
}
}
5.3 Default Initialization and Destroy Methods(默認初始化和銷燬方法)
可以自定義回調方法,不需要去實現接口,不依賴spring的API,降低了系統的耦合,非侵入性。
可以指定一個初始化方法和銷燬方法來實現方法回調
有兩種設置方式,XML和註解
5.3.1 XML(init-method和destroy-method):
dao:
@Repository
public class IndexDaoImpl1 implements IndexDao {
public IndexDaoImpl1() {
System.out.println("Constructor");
}
public void init() {
System.out.println("init");
}
public void destory() {
System.out.println("destory");
}
}
test:
public class Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext classPathXmlApplicationContext
= new ClassPathXmlApplicationContext("classpath:spring.xml");
}
}
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
default-init-method="init"
default-destroy-method="destory" >
<bean id="dao" class="priv.cy.dao.IndexDaoImpl1"></bean>
</beans>
就是直接在需要添加回調方法的類中編寫自己想要的方法就行了,方法名自定義,只需要在XML配置文件中指定初始化回調方法和銷燬回調方法的方法名就可以了
5.3 2 註解(@PostConstruct和@PreDestory):
也可以直接使用註解,不需要用XML配置
@PostConstruct 由JSR-250提供,在構造函數執行完之後執行,等價於xml配置文件中bean的initMethod
@PreDestory 由JSR-250提供,在Bean銷燬之前執行,等價於xml配置文件中bean的destroyMethod
使用以上兩個註解直接用在回調方法上,也能實現自定義的回調方法
例如:
@Repository
public class IndexDaoImpl1 implements IndexDao {
public IndexDaoImpl1() {
System.out.println("Constructor");
}
@PostConstruct
public void init() {
System.out.println("init");
}
@PreDestory
public void destory() {
System.out.println("destory");
}
}
懶加載
只有在調用的時候纔會被加載
使用@Lazy標籤
例如將上面的回調方法代碼加上@Lazy標籤:
@Repository
@Lazy
public class IndexDaoImpl1 implements IndexDao {
public IndexDaoImpl1() {
System.out.println("Constructor");
}
@PostConstruct
public void init() {
System.out.println("init");
}
public void destory() {
System.out.println("destory");
}
}
Test:
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext
= new AnnotationConfigApplicationContext(Appconfig.class);
//IndexDaoImpl1 dao = (IndexDaoImpl1) annotationConfigApplicationContext.getBean(IndexDao.class);
}
}
執行結果:
這個代碼的構造方法和回調方法都不會執行了,說明只有當調用這個bean的時候這個bean在容器中才會被實例化
六、其他
6.1 @ComponentScan標籤的其他用法
它不光可以指定要spring要掃描註解的範圍,還可以指定spring不掃描的位置
使用註解的時候如果向註解中傳入的參數只有一個,也就是value,那麼就可以不用寫value="",直接在括號裏面寫上參數就可以。但是如果傳入的參數大於1,那麼就必須表明每一個參數要傳給誰
例如:
@Configuration
@ComponentScan(value = "priv", excludeFilters = {@ComponentScan.Filter(type = FilterType.REGEX,pattern = "priv.cy.service.*")})
public class Appconfig {
}
上面的註解意思就是掃描priv下面的所有包,但是priv.cy.service包下的內容除外,如果使用註解來裝配bean的話,priv.cy.service包下的bean就會全部裝配失敗。type表示的是後面pattern表達式的類型,像REGEX類型的表達式後面必須要帶*,其他類型的相關簡介請看官方文檔https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-scanning-filters
還有就是可以可以指定必須要掃描哪些位置,使用includeFilters 參數
例如:
@Configuration
@ComponentScan(basePackages = "org.example",
includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
excludeFilters = @Filter(Repository.class))
public class AppConfig {
...
}
它的應用場景比如我對A包裏面除了其中一個子包a需要掃描之外,其餘的都不需要掃描,就需要用到includeFilters。
實現以上功能還可以使用XML配置
<beans>
<context:component-scan base-package="org.example">
<context:include-filter type="regex"
expression=".*Stub.*Repository"/>
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
</beans>
6.2 byType多個同類型的Bean衝突解決辦法
再來說一下之前講過使用byType會出現多個相同類型的bean出現衝突無法自動裝配的解決方法,有兩種
6.2.1、@Primary
這個是主數據源標籤,出現衝突的情況下優先使用這個bean進行裝配
例子:
service:
@Service
public class IndexService {
@Autowired
private IndexDao indexDao;
}
dao:
@Repository
public class IndexDaoImpl1 implements IndexDao {
public IndexDaoImpl1() {
System.out.println("Constructor 1");
}
@PostConstruct
public void init() {
System.out.println("init 1");
}
public void destory() {
System.out.println("destory 1");
}
}
@Repository
public class IndexDaoImpl2 implements IndexDao {
public IndexDaoImpl2() {
System.out.println("Constructor 2");
}
@PostConstruct
public void init() {
System.out.println("init 2");
}
public void destory() {
System.out.println("destory 2");
}
}
不使用@Primary標籤,報錯
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'priv.cy.dao.IndexDao' available: expected single matching bean but found 2: indexDaoImpl1,indexDaoImpl2
對IndexDaoImpl1使用@Primary標籤
@Repository
@Primary
public class IndexDaoImpl1 implements IndexDao {
public IndexDaoImpl1() {
System.out.println("Constructor 1");
}
@PostConstruct
public void init() {
System.out.println("init 1");
}
public void destory() {
System.out.println("destory 1");
}
}
就可以正常注入了,會優先將IndexDaoImpl1注入到IndexService對象中
6.2.2、第二種方法使用限定註解@Qualifier
@Qualifier這個註解可以直接指定要注入哪個bean,它通過name去指定bean
代碼:
@Service
public class IndexService {
@Qualifier("indexDaoImpl1")
private IndexDao indexDao;
}
直接指定使用indexDaoImpl1這個name的bean去注入
Java自帶的一些註解可以替代spring的註解來完成相同的功能,比如@Resource,這個就屬於jdk的註解,但是也能實現自動裝配的功能。
6.3 加速spring掃描類的速度
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-indexer</artifactId>
<version>5.2.4.RELEASE</version>
<optional>true</optional>
</dependency>
</dependencies>
添加了這個之後spring項目在編譯的時候就會將所有要掃描的類添加到一個索引當中,以後再掃描就不需要使用類路徑一個個掃描了,而是直接使用索引進行掃描,這樣大大加快了掃描速度。
6.4 @Bean註解
@Bean 註解在方法上,聲明當前方法的返回值爲一個bean,替代xml中的方式(方法上),也就是將方法的返回值所謂一個bean交給spring容器管理
比如一些必須要通過一個方法返回出來的對象想把它交給spring容器去管理,我們就需要用這個辦法,在這個方法上面添加@Bean標籤,這個方法的返回值就交給spring容器管理了。
配置類:
@Configuration
public class SpringConfiguration {
/**
* 在JavaConfig配置類中使用@Bean標籤來完成bean的註冊,效果就和在XML中配置<bean>標籤是一樣的
*/
@Bean
public HollowController hollowController(){
return new HollowController();
}
}
6.5 @Profile標籤
這個標籤用來切換環境
dao:
@Repository
@Profile("dao1")
public class IndexDaoImpl1 implements IndexDao {
public IndexDaoImpl1() {
System.out.println("Constructor 1");
}
@PostConstruct
public void init() {
System.out.println("init 1");
}
public void destory() {
System.out.println("destory 1");
}
}
@Repository
@Profile("dao2")
public class IndexDaoImpl2 implements IndexDao {
public IndexDaoImpl2() {
System.out.println("Constructor 2");
}
@PostConstruct
public void init() {
System.out.println("init 2");
}
public void destory() {
System.out.println("destory 2");
}
}
給兩個dao設置了對應的環境名
測試類:
public class Test {
public static void main(String[] args) {
// 創建AnnotationConfigApplicationContext類的時候先不指定配置類
AnnotationConfigApplicationContext annotationConfigApplicationContext
= new AnnotationConfigApplicationContext();
// 獲取annotationConfigApplicationContext對象的環境並對其進行修改
annotationConfigApplicationContext.getEnvironment().setActiveProfiles("dao2");
// 指定完環境之後再註冊配置類
annotationConfigApplicationContext.register(Appconfig.class);
// 註冊完成後刷新annotationConfigApplicationContext
annotationConfigApplicationContext.refresh();
// 再取bean就是在dao2這個環境下獲取了
annotationConfigApplicationContext.getBean(IndexService.class);
}
}
此時service注入的就是dao2環境下的bean了
這個註解的用途就是可以設置很多種環境,可以通過切換不同的環境來使不同的bean交給spring容器管理,或者設置多個配置類環境,可以根據需要任意切換。