Spring學習筆記-IoC容器

吉林烏拉發表於2015-10-09
  • IoC全稱為控制反轉(Inverse of Control),也叫依賴注入,是spring容器的核心,AOP、事務等都依賴於此技術。IoC說白了,就是將物件與物件之間的依賴關係從程式碼中轉移到spring的配置檔案中(預設為ApplicationContext.xml 也可以自定義名字),從而由spring進行管理。這樣的好處就是降低了物件與物件之間的依賴。IoC的工作原理就是利用Java的反射功能例項化物件與物件的依賴。除此之外,IoC容器還提供了物件的例項快取、生命週期管理、物件例項代理、事件釋出、資源裝載等高階功能。

  • BeanFactory介面也就是大家熟知的Bean工廠,它是spring框架最核心的介面,它提供了高階的IoC配置機制,可以管理任何不同型別的Java物件。ApplicationContext介面是繼BeanFactory介面之外,另一個重要的介面。它的功能是建立在BeanFactory介面之上的。它除了已有的功能外,還提供國際化支援和框架事件等更多面嚮應用的功能。對於兩者的區別用最直觀最簡介的方式來說就是BeanFactory是Spring框架的核心,它是給Spring用的,而ApplicationContext介面是給使用Spring框架的開發者用的。

  • BeanFactory是一個類工廠,但它和一般的類工廠不同,一般的類工廠是例項化一個類或幾個類,而BeanFactory是個超級工廠,可以建立管理各種類。所有被BeanFactory管理的物件在Spring中統稱為Bean.下面我們初始化BeanFactory工廠,並在工廠中獲取某個物件。


  • spring配置檔案:bean.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:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    <!-- 給car的所有屬性賦值 -->
    <bean id="car" class="com.xiaobai.spring.mark.Car" p:brand="眾泰T600S" p:color="白色" p:speed="120"></bean>

</beans>

  • 實體類Car.java

public class Car {
    /** 品牌 */
    private String brand;

    /** 顏色 */
    private String color;

    /** 速率 */
    private int speed;

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public int getSpeed() {
        return speed;
    }

    public void setSpeed(int speed) {
        this.speed = speed;
    }

    @Override
    public String toString() {
        return "Car{" +
                "brand=`" + brand + ``` +
                ", color=`" + color + ``` +
                ", speed=" + speed +
                `}`;
    }
}

  • 測試類CarTest.java

public class CarTest {

    /**
     * new 方式例項化car物件
     */
    @Test
    public void newCreateCar() {
        Car car = new Car();
        car.setBrand("捷豹xjl");
        car.setColor("黑色");
        car.setSpeed(200);
        System.out.println(car);
    }

    /**
     * BeanFactory 方式例項化car物件
     */
    @Test
    public void beanFactoryCreateCar() {
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        Resource resource = resourcePatternResolver.getResource("classpath:xiaobai/beans.xml");
        BeanFactory beanFactory = new XmlBeanFactory(resource);
        Car car = beanFactory.getBean("car", Car.class);
        System.out.println(car);
    }
    
    /**
     * applicationContext 方式例項化car物件
     */
    @Test
    public void applicationContextCreateCar() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:xiaobai/beans.xml");
        Car car = applicationContext.getBean("car", Car.class);
        System.out.println(car);
    }
}

  • XmlBeanFactory通過Resource裝載Spring配置資訊並啟動IoC容器,然後通過BeanFactory中的getBean()方法從IoC容器中獲取Bean.根據Spring的內部機制,啟動IoC容器時,並不會初始化配置檔案中的Bean.當第一次呼叫getBean()時初始化。對於單例的Bean來說,BeanFactory會快取Bean例項,在第二次呼叫getBean()時,直接從IoC容器的快取中獲取Bean例項。接下來我們通過下面的例子來證明BeanFactory的快取機制。


  • BeanFactory的快取測試

ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
Resource resource = resourcePatternResolver.getResource("classpath:xiaobai/beans.xml");
BeanFactory beanFactory = new XmlBeanFactory(resource);
Car car1 = beanFactory.getBean("car", Car.class);
Car car2 = beanFactory.getBean("car", Car.class);
System.out.println(car1);
System.out.println(car2);
  • 上述程式碼我們從IoC容器中獲取了兩個Car,通過輸出物件的記憶體地址資訊來判斷這兩個car是否為同一個

  • 輸出結果

com.xiaobai.spring.mark.Car@1d96f4b5
com.xiaobai.spring.mark.Car@1d96f4b5
  • 通過輸出結果證明了我們之前所說的,BeanFactory的快取機制。但在各別情況下我們不希望它有快取,我們希望每次訪問時返回的都是一個新的物件。那怎麼辦呢。我們可以通過scope屬性來配置。

  • beanx.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:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 給car新增scope屬性並值設定為prototype -->
    <bean id="car" class="com.xiaobai.spring.mark.Car" p:brand="眾泰T600S" p:color="白色" p:speed="120" scope="prototype"></bean>
</beans>
  • 輸出結果

com.xiaobai.spring.mark.Car@6ecf829d
com.xiaobai.spring.mark.Car@79884a40
  • 通過結果我們看,通過給Bean設定scope=”prototype”時,IoC容器每次返回的都是一個新物件了,如果想每次返回的都是同一個物件,可以設定為scope=”singleton”,預設不設定scope屬性時scope=”singleton”


  • Spring3.0以後支援類註解的配置方式,可以通過@Configuration註解為Spring提供配置資訊,Spring為配置的註解類提供了一個專門的實現類AnnotationConfigApplicationContext,下面我們來看一下怎麼來載入配置資訊

  • Beans.java

@Configuration
public class Beans {
    @Bean(name = "car")
    public Car buildCar() {
        Car car = new Car();
        car.setBrand("東風標誌508");
        car.setColor("白色");
        car.setSpeed(300);
        return car;
    }
}
  • 測試用例

/**
 * @Configuration 方式例項化car物件
 */
@Test
public void configurationCreateCar() {
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Beans.class);
    Car car = applicationContext.getBean("car", Car.class);
    System.out.println(car);
}
  • 輸出結果

Car{brand=`東風標誌508`, color=`白色`, speed=300}

  • WebApplicationContext介面是Spring專門為Web應用準備的,它繼承了ApplicationContext介面,併為Bean的scope屬性增加了三個值分別是request、session、globalSession.它允許從Web根目錄的路徑中裝載配置檔案,完成初始化工作。因為它是為Web服務的,所以它必須依賴於Web容器。根據Web專案開發經驗,我們應該在web.xml配置自啟動的Servlet或者Web容器監聽器,來配置Web專案開發環境。Spring為這兩種都提供了相應的類,來啟動Spring容器。它們分別為org.springframework.web.context.ContextLoaderListener和org.springframework.web.context.ContextLoaderServlet.我們可以根據這兩種中的任何一種方式來啟動WebApplicationContext。雖然兩種沒有什麼太大的區別,都可以啟動WebApplicationContext,但是隻有Servlet2.3以上的版本的Web容器才支援Web容器的監聽器。(包括當前版本),所以可以根據專案需要配置啟動WebApplicationContext的方式。下面我們來看一下具體的配置程式碼


  • web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
    <!--
        下面的程式碼是選擇Spring配置檔案的路徑,也可以定義Spring配置檔案的名字
        如果不配置Spring預設在專案根目錄下查詢applicationContext.xml檔案
    -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <!--
            如果配置多個檔案可以用逗號分隔,也可以用資源字首的方式配置
            ? : 匹配檔名中的一個字元
            * : 匹配檔名中的任意個字元
           ** : 匹配多層路徑
           classpath:com/t?st.xml 匹配com類路徑下的com/tast.xml、tbst.xml 等
           classpath:com/*.xml 匹配com類路徑下的所有xml檔案
           classpath:com/**.xml 匹配com類路徑及其子目錄下的所有xml檔案
        -->
        <param-value>classpath:beans.xml</param-value>
    </context-param>
    
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

</web-app>

  • 在SpringIoC容器中一個Bean對應配置檔案中的一個<bean>,id為這個Bean的名稱,通過容器的getBean()方法獲取對應的Bean。id在IoC容器中必須是唯一的,它的命名還要滿足XML對id的命名規範:必須以字母開始,後機可以是字母、數字、連字元、下劃線、等。但是,在特殊情況下,我們需要特別的名字,那id就不允許了。這時我們可以用name屬性。name屬性沒有字元限制,幾乎可以使用任何字元,並可以設定多個名字。如下所示

<bean name="#1, $2" class="com.xiaobai.spring.mark.Car" p:brand="眾泰T600S" p:color="白色" p:speed="120"/>
  • Spring配置檔案雖然不允許出現兩個id的<bean>,但卻可以出現兩個name的<bean> 如果有多個name相同的<bean> 獲取時,將返回最後宣告的那個Bean,原因是後面的<bean>覆蓋了前面的<bean>.如果id和name兩個屬性都未指定,Spring自動將class做為Bean的名稱。如下所示

<bean class="com.xiaobai.spring.mark.Car" p:brand="眾泰T600S" p:color="白色" p:speed="120"/>
@Test
public void applicationContextCreateCar() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:xiaobai/beans.xml");
    Car car = applicationContext.getBean("com.xiaobai.spring.mark.Car", Car.class);
    System.out.println(car);
}
  • 執行結果

Car{brand=`眾泰T600S`, color=`白色`, speed=120}

相關文章