使用Spring的IOC進行Bean管理

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的懶加載功能

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章