Spring 是一個爲了解決企業應用開發的複雜性而創建的開源框架。是一個輕量級的控制反轉( IOC)和麪向切面( AOP )的容器框架:
- 從大小與開銷兩方面而言 Spring 都是輕量的
- 通過控制反轉( IOC )的技術達到鬆耦合的目的
- 提供了面向切面編程的豐富支持,允許通過分離應用的業務邏輯與系統級服務進行內聚性的開發
- 包含並管理應用對象的配置和生命週期的容器
- 將簡單組件組合稱爲複雜的框架
1、創建maven項目
首先通過IDEA創建一個Maven類型的Spring項目,點擊File->new->Project彈出如下界面,選擇maven,點擊next
之後爲項目選擇名稱和位置,並且可以設置項目所屬公司名GroupId、輸出名ArtfactId、版本名Version,最後生成的目錄結構如下右圖所示, 在src目錄下有main和test兩個文件夾,main用於存放源文件,test/java測試模塊,通過JUnit生成的測試文件會自動保存到該目錄。main下的java文件夾爲Source Root,resources爲Resources Root文件夾存放配置文件。最外層pom.xml爲maven的配置文件。
2、使用接口降低耦合度
如下所示我創建了兩個汽車類Audi和Toyota,分別有三個方法start()、run()、stop()。還有一個人XiaoWang,駕駛汽車回家goHome(),首先需要創建汽車類對象audi,然後再通過audi調用方法回家。
public class Audi {
public void start(){
System.out.println("奧迪啓動!");
}
public void run(){
System.out.println("奧迪行駛中...");
}
public void stop(){
System.out.println("奧迪停車!");
}
}
public class XiaoWang {
private Audi audi=new Audi();
public void goHome(){
audi.start();
audi.run();
audi.stop();
}
}
這時如果Xiaowang希望更換車輛Toyota,他就需要再創建一個新的對象toyota,並且重寫一個goHome方法去調用相同的start()、run()、stop()方法,這是十分繁瑣與不便的。造成這種情況的原因是XiaoWang與汽車類Audi之間的耦合度過高。通過使用接口將耦合在一起的整體拆分爲兩部分,可以將汽車Audi、Toyota抽象爲一個接口Car,然後XiaoWang通過傳入Car對象來使用不同的汽車。
//公共接口
public interface Car {
void start();
void run();
void stop();
}
//汽車類實現接口
public class Toyota implements Car{
public void start(){
System.out.println("豐田啓動!");
}
public void run(){
System.out.println("豐田行駛中...");
}
public void stop(){
System.out.println("豐田停車!");
}
}
//使用接口調用汽車
public class XiaoWang {
private Car car;
public XiaoWang(Car car) {
this.car = car;
}
public void goHome(){ //通過Car接口來調用汽車類的方法
car.start();
car.run();
car.stop();
}
}
public void main() {
Car car =new Toyota(); //創建傳入不同的car對象來使用
XiaoWang xiaoWang=new XiaoWang(car);
xiaoWang.goHome();
}
3、IOC
控制反轉(IOC,Inversion of Controll)是一個重要的面向對象編程的規則,用以削弱計算機程序的耦合問題,也是輕量級 Spring 框架的核心。所謂控制反轉是指應用程序本身不負責所需對象的創建和維護,而是由外部容器完成,應用程序直接拿來使用。如下圖所示,我們將一個普通Java對象(POJOs,Plain Ordinary Java Objects)傳入Spring容器,通過讀取配置元數據,之後就會生產一個符合我們系統需要的並且可以直接使用的對象。通過使用IOC,我們不必再手動創建和管理對象了,並且可以直接使用一個對象
依賴注入(Dependency Injection)是Spring實現IOC的一種方式,即在容器運行期間,動態地將依賴關係注入到對象。
手動實現IoC容器
如下所示爲手動實現的Ioc容器,它利用一個ConcurrentHashMap來保存字符串類型的beanId到對應Bean類的映射關係。其getBean()方法即通過beanId查找Map中對應的Bean並且返回。setBean()方法來創建新的bean存入到Map中,參數除了需要傳入bean所屬類型clazz和beanId標識外,還需要傳入其構造方法所需要的bean,例如構造一個XiaoWang對象,需要傳入一個Car作爲參數。之後根據參數beanId從Map中取出對應的bean,將其傳入clazz的構造方法,遍歷clazz所有的構造方法找到合適的實例化bean。最後將實例化的bean存入Map。
public class IocContainer {
private Map<String, Object> beans = new ConcurrentHashMap<String, Object>();
/**
* 根據beanId返回一個Bean
* @param beanId 要獲取的bean的Id
* @return 返回找到的bean
*/
public Object getBean(String beanId) {
return beans.get(beanId);
}
/**
* 創建一個bean
* @param clazz 要創建的bean的類型
* @param beanId 對應的beanId
* @param paramBeanIds 對應構造方法所需要的參數的beanId列表
*/
public void setBeans(Class clazz, String beanId, String... paramBeanIds) {
//1、獲取構造方法所需的參數bean
Object[] paramBeans = new Object[paramBeanIds.length];
for (int i = 0; i < paramBeanIds.length; i++) {
paramBeans[i] = beans.get(paramBeanIds[i]);
}
//2、調用構造方法實例化bean
Object bean = null;
for (Constructor constructor : clazz.getConstructors()) {
try { // 依次遍歷clazz類的所有構造方法找到合適的來實例化bean
bean = constructor.newInstance(paramBeans);
} catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
e.printStackTrace();
}
}
if (bean == null)
throw new RuntimeException("沒有合適的構造方法實例化" + beanId);
//3、將bean保存到beans中
beans.put(beanId, bean);
}
}
通過一個Test來測試上面的IOC容器,在使用之前需要創建bean,然後在testIoc()方法中就可以直接使用bean而不需要創建和維護對象。
class IocContainerTest {
private IocContainer iocContainer=new IocContainer();
@BeforeEach
void setUp() {
//在使用之前創建bean
iocContainer.setBeans(Audi.class,"audi");
iocContainer.setBeans(Toyota.class,"toyota");
//傳入Bean參數audi構造XiaoWang
iocContainer.setBeans(XiaoWang.class,"xiaowang","audi");
}
@Test
void testIoc() {
XiaoWang x1=(XiaoWang)iocContainer.getBean("xiaowang"); //直接獲取並使用bean
x1.goHome();
}
}
Spring中的IOC
spring中已經集成了Ioc容器供我們使用。其使用步驟如下:
1、首先在maven的配置文件pom.xml中引入Spring的依賴,只需要輸入<artifactId>標籤spring-core、spring-context,IDEA會提示補全對應的<groupId>和<version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.4.RELEASE</version>
</dependency>
......
2、在項目的src/main/resources文件夾下創建配置文件spring-ioc.xml,在其中註冊所需要交友Ioc容器管理的Bean類,在<bean>標籤內輸入bean的id和對應的類
<?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="audi" class="com.spring.ioc.Audi"/>
</beans>
3、獲取並使用bean。首先通過ApplicationContext加載resources目錄下的配置文件spring-ioc.xml,然後通過getBean()從Ioc容器獲取bean,需要傳入bean的Id和Class作爲參數
class AudiTest {
@Test
void run() {
//通過上下文管理類獲取配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("spring-ioc.xml");
//獲取Bean並使用
Audi audi = context.getBean("audi", Audi.class);
audi.run();
}
}
4、使用Bean
實例化
Spring實例化一個bean有三種方法:
- 第一種是通過構造方法實例化Bean,即之前使用的方法。如下所示,首先創建一個Bean類
public class Bean {
public Bean() {
System.out.println("Bean被創建");
}
}
之後在配置文件中註冊:
<bean id="bean1" class="com.spring.ioc.Bean"/>
最後在代碼中使用Bean如下
ApplicationContext context = new ClassPathXmlApplicationContext("spring-ioc.xml");
Bean bean1=context.getBean("bean1",Bean.class);
- 第二種是通過工廠的靜態方法來實例化Bean。在Bean的基礎上再創建一個BeanFactory來返回bean對象
public class BeanFactory {
public static Bean getBean(){
return new Bean();
}
}
接着在配置文件中註冊,這裏的factory-method要求必須爲靜態方法
<bean class="com.spring.ioc.BeanFactory" factory-method="getBean" id="bean2"/>
最後採用相同的方式獲取bean
Bean bean2=context.getBean("bean2",Bean.class);
- 第三種方法是通過實例化工廠來實例Bean,首先工廠的getBean()方法是非靜態的
public class BeanFactory {
public Bean getBean(){
return new Bean();
}
}
接着在配置文件中同時註冊Bean和BeanFactory,並在bean中配置其對應的factory-bean和factory-method
<bean id="bean3Factory" class="com.spring.ioc.BeanFactory"/>
<bean id="bean3" class="com.spring.ioc.Bean" factory-bean="bean3Factory" factory-method="getBean"/>
最後採用相同的方法獲取bean
Bean bean3=context.getBean("bean3",Bean.class);
爲Bean添加別名可以通過name屬性,也可以通過<alias>標籤。通過別名創建的bean其實指向同一個bean對象。
<bean id="bean1" class="com.spring.ioc.Bean" name="bean1_1,bean1_2"/>
<alias name="bean1" alias="bean1_3"/>
屬性注入
對Bean的屬性進行注入有兩種方法,第一種是通過構造函數進行注入,第二種是通過setXxxv()設置屬性。
如下所示爲一個bean類XiaoWang,他有兩個屬性age、audi,其中audi爲自定義的汽車Audi對象,並且設置其構造方法和get/set方法如下
public class XiaoWang {
private int age;
private Audi audi;
public XiaoWang(int age, Audi audi) {
this.age = age;
this.audi = audi;
}
public int getAge() { return age; }
public void setAge(int age) {this.age = age; }
public Audi getAudi() {return audi; }
public void setAudi(Audi audi) {this.audi = audi; }
}
由於這裏的構造方法需要傳入兩個參數,因此需要在配置文件中通過<constructor-arg>完成參數的傳入。其中屬性index代表是第幾個參數,name代表成員變量的名稱,type指定參數的類型,這裏使用index即可指明對應哪個屬性,因此name和type可以省略。在爲參數賦值時,如果是Java自帶數據類型,則使用value,如果是我們自己創建的類,則需要使用ref="",並傳入對應的bean。這裏第一個爲age賦值爲24,第二個賦值bean爲之前創建的audi1
除了使用構造方法,還可以使用<property>對Bean的屬性賦值。它會調用bean的setXxx()方法完成對屬性的賦值。
<bean id="audi1" class="com.spring.ioc.Audi"/>
<bean id="xiaowang" class="com.spring.ioc.XiaoWang">
<constructor-arg index="0" name="age" value="24"/>
<constructor-arg index="1" name="audi" type="com.spring.ioc.Audi" ref="audi1"/>
<property name="age" value="21"/>
<property name="audi" ref="audi1"/>
</bean>
可以採用<constructor>和<property>的簡便寫法,首先在xml配置文件頭中引入xmlns:c、xmlns:p,接着就可以在<bean>的屬性中使用c:、p:來表示構造方法和屬性。例如c:age代表傳入屬性age的構造方法,c:audi-ref代表傳入屬性audi的構造方法,p:age代表傳入age的setAge()方法完成對age的賦值。注意由於audi並非Java的基本數據類型,所以其使用一般會帶有“ref”之類的標識。
<bean id="audi1" class="com.spring.ioc.Audi"/>
<bean id="xiaowang" class="com.spring.ioc.XiaoWang"
c:age="22" c:audi-ref="audi1"
p:age="23" p:audi-ref="audi1"/>
Spring可以通過自動裝配完成屬性的注入,設置<beans default-autowire="byName" ...>代表通過beanId名完成自動裝配,例如XiaoWang有一個屬性audi,spring會自動查找Id爲audi的Bean並且通過setAudi()方法注入到屬性。如果beanId爲audi111則查找失敗。如果default-autowire="byType"代表依據類型查找,這時會查找類型爲Audi的bean並完成注入。
如果Bean的屬性是Java複合數據類型使用<property>賦值如下。在Bean XiaoWang中有string類型的List和Car類型的List屬性,並且具有對應的get/set方法。在配置文件中賦值如下,常規類型的值使用<value>,自定義類型用<ref>。Set類型和List類似,只需要將<list>換爲<set>即可。
<property name="stringList">
<list>
<value>strig1</value>
<value>字符串2</value>
</list>
</property>
<property name="carList">
<list>
<ref bean="audi1"/>
<ref bean="toyota1"/>
</list>
</property>
Map類型賦值如下,Bean中有Map類型的屬性stringMap和carMap,分別使用<map>標籤賦值
<property name="stringMap">
<map>
<entry key="sk1" value="string1"/>
</map>
</property>
<property name="carMap">
<map>
<entry key="car1" value-ref="audi1"/>
</map>
</property>
如果希望賦空值,只需要shiyong<null/>即可,<property name="carMap"> <null/> </property>
在使用屬性時可以創建內部Bean,類似Java的內部類,供臨時bean屬性賦值使用
<bean id="xiaowang" class="com.spring.ioc.XiaoWang">
<property name="age" value="21"/>
<property name="audi">
<bean class="com.spring.ioc.Audi"/> <!--內部bean-->
</property>
</bean>
屬性繼承
如果兩個Bean之間存在繼承關係,可以使用parent屬性指明Bean的父標籤,從而可以繼承父類的屬性。例如下面有父類、子類兩個Bean:
public class ParentBean {
protected String parentString;
public String getParentString() {
return parentString;
}
public void setParentString(String parentString) {
this.parentString = parentString;
}
}
public class ChildBean extends ParentBean { //繼承自父類
private String childString;
public String getChildString() {
return childString;
}
public void setChildString(String childString) {
this.childString = childString;
}
@Override
public String toString() { //打印父類和子類的屬性
return "ChildBean{" +
"childString='" + childString + '\'' +
", parentString='" + parentString + '\'' +
'}';
}
}
在配置文件中配置繼承關係,父類<bean>設置abstract="true"代表不需要實例化該Bean,子類<bean>通過parent="paren"將父類指向了id爲parent的<bean>。打印輸出子類child:ChildBean{childString='子類字符串', parentString='父類字符串'},可以看到父類和子類的屬性都被輸出。
<bean id="parent" class="com.spring.ioc.ParentBean" abstract="true">
<property name="parentString" value="父類字符串"/>
</bean>
<bean id="child" class="com.spring.ioc.ChildBean" parent="parent">
<property name="childString" value="子類字符串"/>
</bean>
初始化和銷燬
如果我們希望在Bean的初始化和銷燬之前執行一些操作有兩種方法,
第一是在<bean>中使用init-method、destroy-method來指定Bean在初始化和銷燬時執行的方法,也可以在<beans>中通過屬性default-init-method、default-destroy-method爲所用的bean設置默認的方法。例如在Bean類中定義onInit()、onDestroy()方法,並在spring文件中進行配置:
<bean id="bean" class="com.spring.ioc.Bean" init-method="onInit" destroy-method="onDestroy" />
之後通過上下文對象context獲取bean對象並且在關閉context時銷燬
AbstractApplicationContext context=new ClassPathXmlApplicationContext("spring-ioc.xml");
Bean bean1=context.getBean("bean",Bean.class);
System.out.println(bean1);
context.close(); //關閉上下文對象context時會調用bean的銷燬方法
第二種方法是通過實現InitializingBean、DisposableBean接口中的方法來執行創建、銷燬之前的操作
public class Bean implements DisposableBean, InitializingBean {
@Override
public void destroy() throws Exception {
System.out.println("實現接口,Bean銷燬之前");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("實現接口,Bean創建之前");
}
}
5、作用域
單例/多例模式
在<bean>標籤中的scope屬性可以設置bean的作用域,其默認值爲singleton單例模式,與之對應的是prototype多例模式。所謂單例模式就是指在一個上下文對象context中只會實例化一個bean,而多例模式會創建多個不同的bean。
如下所示爲一個Bean類,它有一個InnerBean類的成員變量
public class Bean {
private InnerBean innerBean;
public Bean() {
System.out.println("Bean被創建");
}
public InnerBean getInnerBean() {
return innerBean;
}
public void setInnerBean(InnerBean innerBean) {
this.innerBean = innerBean;
System.out.println("內部bean:"+innerBean);
}
}
public class InnerBean {
public InnerBean() {
System.out.println("InnerBean被創建");
}
}
在配置文件中配置innerBean和bean,並且將innerBean設置爲bean的子屬性,這裏innerBean的scope爲單例模式singleton
<bean id="innerBean" class="com.spring.ioc.InnerBean" scope="singleton"/>
<bean id="bean" class="com.spring.ioc.Bean">
<property name="innerBean" ref="innerBean"/>
</bean>
在測試模塊中先創建兩個innerBean實例,之後再創建一個bean實例
@Test
void testScope() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-ioc.xml");
InnerBean inner1 = context.getBean("innerBean", InnerBean.class);
System.out.println("innerBean1:" + inner1);
InnerBean inner2 = context.getBean("innerBean", InnerBean.class);
System.out.println("innerBean2:" + inner2);
Bean bean = context.getBean("bean", Bean.class);
System.out.println("bean:" + bean);
}
運行結果如下所示,InnerBean被創建了一次,並且inner1、inner2以及bean內部innerBean所使用的都是同一個InnerBean
將配置文件中的innerBean設置爲多例模式scope="prototype",再次運行代碼結果如下,可見InnerBean被創建了三次,並且inner1、inner2以及bean內部innerBean所使用的是不同的InnerBean對象
Web作用域
web中常用的爲request、session、application三個作用域。如下所示爲定義一個Servlet的Controller,定義請求映射的路徑爲requestScope,並向頁面返回本對象的toString()結果
@Controller
public class RequestController {
@RequestMapping("requestScope")
@ResponseBody
public String testScope(){
return this.toString();
}
}
將上面的Controller交由Spring管理並配置其作用域爲request,部署服務器後訪問該Controller頁面,刷新頁面可以看到兩次request請求訪問返回的對象不同,這是因爲RequestController的作用域爲request
<bean class="com.spring.ioc.controller.RequestController" scope="request"/>
同理定義一個SessionController,並定義其作用域爲session。刷新頁面返回的SessionController對象相同,但是在另一個瀏覽器訪問sessionScope對象不同,這樣由於同一個瀏覽器session相同,換一個瀏覽器會打開一個新的session
<bean class="com.spring.ioc.controller.RequestController" scope="request"/>
定義一個ApplicationController,其作用域爲application。不論是刷新頁面還是在不同的瀏覽器,都會返回相同的對象,因爲只要服務器沒有關閉,就屬於同一個application
<bean class="com.spring.ioc.controller.ApplicationController" scope="application"/>
自定義作用域
spring支持用戶自定義作用域,類需要實現springframework的Scope接口。接口中的get()方法用於從作用域中返回對象,remove()方法用於從作用於刪除並返回對象。之後在配置文件中註冊自定義scope,例如spring爲我們提供了一個自定義好的scope--SimpleThreadScope用於在一個線程中返回一個實例,其註冊如下:
<!--引入自定義scope-->
<bean id="simpleThreadScope" class="org.springframework.context.support.SimpleThreadScope"/>
<!--配置scope-->
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="simpleThread" value-ref="simpleThreadScope"/>
</map>
</property>
</bean>
<!--使用自定義scope-->
<bean id="bean1" class="com.spring.ioc.Bean" scope="simpleThread"/>
Bean的懶加載
如果bean的作用域爲默認的singleton,則spring會在啓動時就會實例化該bean。可以通過設置lazy-init="true"來使Bean懶加載,即當我們使用getBean()方法獲取時纔會創建bean。如果希望所有的bean都是懶加載,在<beans>標籤中設置default-lazy-init="true"
<bean id="bean" class="com.spring.ioc.Bean" lazy-init="true"/>
6、使用註解
爲了避免使用繁瑣的配置文件對Bean進行管理,Spring在2.5版本之後引入了註解的方式對Bean進行配置和管理。例如有一個類Bean1,我們首先創建Spring的上下文管理器BeanConfiguration用於管理Bean,爲其添加註解@Configuration。在其中定義方法myBean1用於返回一個被管理的Bean1類,這裏方法名myBean1就是默認的bean的Id,也可以通過註解@Bean的屬性value爲其添加別名,並且將新創建的Bean1對象作爲返回值。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class BeanConfiguration {
@Bean(value = "myBeanId")
public Bean1 myBean1() {
return new Bean1();
}
}
在代碼中獲取Bean1如下,首先通過AnnotationConfigApplicationContext獲取上下文對象context,其傳入參數爲剛定義的管理類BeanConfiguration。之後通過getBean()得到bean對象b1。
ApplicationContext context=new AnnotationConfigApplicationContext(BeanConfiguration.class);
Bean1 b1=context.getBean("myBean1",Bean1.class);
上面的方法爲每一個Bean都要定義一個返回方法,這樣也很繁瑣。可以添加註解@ComponentScan來掃描包的方式將Bean自動添加到BeanConfiguration類中,這時它會自動掃描指定包目錄下添加了@Component註解的Bean,並將其類名首字母小寫作爲默認bean的Id。例如下面BeanConfiguration會掃描com.spring.ioc.annotation,添加將其中的Bean1,並將bean1作爲其id。也可以通過@Component的value屬性爲其添加自定義Id--”myBeanId“。類似@Component的註解還有@Controller用於標註控制層組件、@Service標註服務處、@Repository標註Dao層
//被管理的類
package com.spring.ioc.annotation;
import org.springframework.stereotype.Component;
@Component(value = "myBeanId")
public class Bean1 {
}
//Bean的管理類
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(value = "com.spring.ioc.annotation") //自動掃描包
public class BeanConfiguration {
}
屬性注入
簡單數據類型
簡單的數據類型可以通過@Value()註解來進行屬性注入,並且完成從string到對應類型的轉換,結果爲:
@Component
public class MyBean {
@Value("101")
private int simpleInt;
自定義類型的屬性注入
對Bean的屬性進行注入有三種方法,第一種是通過構造函數,如下所示,使用@Autowired註解修飾MyBean的構造方法,它會自動去BeanConfiguration管理的上下文中查找ParamBean並完成對屬性paramBean的注入。
@Component
public class MyBean {
private ParamBean paramBean;
@Autowired
public MyBean(ParamBean paramBean) {
this.paramBean = paramBean;
}
}
第二種方法是通過setXxx()方法完成對屬性paramBean的注入
@Component
public class MyBean {
private ParamBean paramBean;
@Autowired
public void setParamBean(ParamBean paramBean) {
this.paramBean = paramBean;
}
}
第三種方法是直接爲屬性添加@Autowired,Spring會自動查找該類型的Bean完成注入
@Component
public class MyBean {
@Autowired
private ParamBean paramBean;
}
複合類型的屬性注入
複合類型的屬性注入也是通過@Autowired修飾,不同的是需要定義對應的Bean來返回相關類型。例如下面定義了一個String類型的List屬性,並在Spring上下文管理器中定義了一個Bean來返回stringList,Spring會將返回的List注入到stringList屬性。我們還可以通過@Component註解創建一個Bean返回stringList,效果相同。
@Component
public class MyBean {
private List<String> stringList;
@Autowired
public void setStringList(List<String> stringList) {
this.stringList = stringList;
}
}
@Configuration
@ComponentScan(value = "com.spring.ioc.annotation")
public class BeanConfiguration {
@Bean
public List<String> stringList(){
List<String> list=new ArrayList<String>();
list.add("string1");
list.add("字符串2");
return list;
}
}
如果在上下文管理器中存在String類型的Bean,那麼會被自動作爲元素添加到String類型的List屬性中,並且可以通過@Order註解來指定元素添加的順序,順序的數字不必連續,也不必從1開始。運行結果如下所示:
@Configuration
@ComponentScan(value = "com.spring.ioc.annotation")
public class BeanConfiguration {
@Bean
@Order(36)
public String stringBean1() {
return "string3";
}
@Bean
@Order(21)
public String stringBean2() {
return "字符串4";
}
}
如果同時存在stringList、string類型的Bean,則會優先將String類型的Bean作爲元素添加到stringList屬性。這時如果仍然希望使用stringList注入,則需要@Qualifier來指定需要注入的beanId
@Autowired
@Qualifier("stringList")
public void setStringList(List<String> stringList) {
this.stringList = stringList;
}
同理可以使用@Autowired來修飾Map數據類型,並定義返回Map類型的Bean來完成注入。
作用域
通過註解的方式爲Bean規定作用域只需要通過@Scope()即可,如下定義MyBean作用域爲多例模式
@Component
@Scope("prototype")
public class MyBean {
......
可以通過CustomScopeConfigurer來添加自定義作用域,如下所示添加自定義作用域SimpleThreadScope
@Bean
public SimpleThreadScope MyScope() {
return new SimpleThreadScope();
}
@Bean
public CustomScopeConfigurer customScopeConfigurer() {
CustomScopeConfigurer configurer = new CustomScopeConfigurer();
configurer.addScope("simpleThread", MyScope());
return configurer;
}
初始化和銷燬
通過註解的方式定義在Bean的初始化和銷燬之前的執行函數有三種方法,第一種是實現InitializingBean、DisposalBean接口,這種方法和之前的XML配置一樣。第二種方法是通過@PostConstruct、@PreDestroy註解來標註方法
@Component
public class MyBean {
......
@PostConstruct
public void onInit(){
System.out.println("創建Bean...");
}
@PreDestroy
public void onDestroy(){
System.out.println("銷燬Bean...");
}
第三種方法是通過@Bean的屬性initMethod和destroyMethod來指定方法
@Bean(initMethod = "onInit",destroyMethod = "onDestroy")
public MyBean myBean(){
return new MyBean();
}
在註解中可以通過@lazy來實現Bean的懶加載功能