-
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}