Spring IoC容器

1、IoC概述

    控制反轉(Inverse of Control,IoC)是Spring容器的內核,AOP、聲明式事務等功能都是在此基礎上擴展的。所謂IoC就是通過容器來控制業務對象之間的依賴關係,而不是傳統實現中,由代碼直接操控。這也就是“控制反轉”概念所在:控制權由應用代碼中轉移到了外部容器,控制權的轉移,就是反轉。控制權轉移帶來的好處就是降低了業務對象之間的依賴程度。

    更加形象的說明一下IoC是如何做的。這有點像通過婚介找女朋友,在我和女朋友之間引入了一個第三者:婚姻介紹所。婚介管理了很多男男女女的資料,我可以向婚介提出一個列表,告訴它我想找個什麼樣的女朋友,比如長得像李嘉欣,身材像林熙雷,唱歌像周杰倫,速度像卡洛斯,技術像齊達內之類的,然後婚介就會按照我們的要求,提供一個mm,我們只需要去和她談戀愛、結婚就行了。簡單明瞭,如果婚介給我們的人選不符合要求,我們就會拋出異常。整個過程不再由我自己控制,而是有婚介這樣一個類似容器的機構來控制。Spring所倡導的開發方式就是如此,所有的類都會在spring容器中登記,告訴spring你是個什麼東西,你需要什麼東西,然後spring會在系統運行到適當的時候,把你要的東西主動給你,同時也把你交給其他需要你的東西。所有的類的創建、銷燬都由 spring來控制,也就是說控制對象生存週期的不再是引用它的對象,而是spring。對於某個具體的對象而言,以前是它控制其他對象,現在是所有對象都被spring控制,所以這叫控制反轉。

2、BeanFactory和ApplicationContext

    Spring通過一個配置文件描述了Bean和Bean之間的依賴關係,利用Java語言的反射功能實例化Bean並建立Bean之間的依賴關係。Spring的IoC容器在完成這些底層工作的基礎上,還提供了Bean實例化緩存、聲明週期管理、Bean實例化代理、事件發佈、資源裝載等高級服務。

    Bean工廠(BeanFactory)是Spring框架最核心的接口,它提供了高級IoC的配置機制。BeanFactory使管理不同類型的Java對象稱爲可能,應用上下文(ApplicationContext)建立在BeanFactory基礎之上,提供了更多面嚮應用的功能。BeanFactory是Spring框架的基礎設施,面向Spring本身;ApplicationContext面向Spring框架的開發者,幾乎所有的應用場合都直接使用ApplicationContext而非底層的BeanFactory。

    ApplicationContext由BeanFactory派生而來,提供了更多面向實際應用的功能。在獲取ApplicationContext實例後,就可以像BeanFactory一樣調用getBean(beanName)返回Bean了。ApplicationContext的初始化和BeanFactory的初始化有一個重大的區別:BeanFactory在初始化容器時,並未初始化Bean,直到第一次訪問某個Bean時才實例化目標Bean;而ApplicationContext在初始化應用上下文時就實例化所有單實例的Bean。因此ApplicationContext的初始化時間會比BeanFactory稍長一些。

    Spring3.0支持基於註解類的配置方式,主要功能來自於Spring的一個名爲JavaConfig的子項目,目前JavaConfig已經是Spring核心框架的一部分。一個標註@Configuration註解的POJO即可提供Spring所需的Bean配置信息。

    WebApplicationContext是專門爲Web應用準備的,它允許從相對於Web根目錄的路徑中裝載配置文件,完成初始化工作。從WebApplicationContext中可以獲得ServletContext的引用,整個Web應用上下文對象將作爲屬性防止到ServletContext中,以便Web應用環境可以訪問spring應用上下文。Spring專門爲此提供了一個工具類WebApplicationContextUtils。通過該類的getWebApplicationContext(ServletContext sc)方法,既可以從ServletContext中獲取WebApplicationContext實例。

    Spring分別提供了用於啓動WebApplicationContext的Servlet和Web容器監聽器

  • org.springframework.web.context.ContextLoaderServlet

  • org.springframework,web.context.ContextLoaderListener

兩者都實現了啓動WebApplicationContext實例的邏輯,用戶只要根據Web容器的具體情況選擇二者之一,並在web.xml中完成配置就可以了。

    通過實例認識BeanFactory

package org.worm.biz;
import org.apache.log4j.Logger;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.worm.util.LogUtil;
/** 
* @ClassName: Car 
* @Description: TODO 認識BeanFactory測試用類
* @author Administrator
* @date 2016年6月28日 上午10:48:51 
*  
*/ 
public class Car implements BeanFactoryAware,BeanNameAware,InitializingBean,DisposableBean{
 private static final Logger logger = LogUtil.getLogger(Car.class);
 private String brand;
 private String color;
 private int maxSpeed;
 private String name;
 private BeanFactory beanFactory;
 private String beanName;
 
 public Car() {
  logger.info("調用Car()構造函數");
 }
 public String getBrand() {
  return brand;
 }

 public void setBrand(String brand) {
  logger.info("調用setBrand()設置屬性");
  this.brand = brand;
 }

 public String getColor() {
  return color;
 }

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

 public int getMaxSpeed() {
  return maxSpeed;
 }

 public void setMaxSpeed(int maxSpeed) {
  this.maxSpeed = maxSpeed;
 }

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }

 public BeanFactory getBeanFactory() {
  return beanFactory;
 }

 public String getBeanName() {
  return beanName;
 }

 public void destroy() throws Exception {
  // TODO Auto-generated method stub
  logger.info("調用DispoasbleBean.destroy().");
 }
 public void afterPropertiesSet() throws Exception {
  // TODO Auto-generated method stub
  logger.info("調用InitializingBean.afterPropertiesSet()");
 }
 public void setBeanName(String name) {
  // TODO Auto-generated method stub
  logger.info("調用BeanNameAware.setBeanName()");
  this.beanName = name;
 }
 public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
  // TODO Auto-generated method stub
  logger.info("調用BeanFactoryAware.setBeanFactory().");
  this.beanFactory = beanFactory;
 }
 @Override
 public String toString() {
  return "Car [brand=" + brand + ", color=" + color + ", maxSpeed=" + maxSpeed + "]";
 }
 public void introduce(){
  logger.info("introduce:"+this.toString());
 }
 public void myInit(){
  logger.info("調用myInit()");
  this.maxSpeed = 240;
 }
 public void myDestory(){
  logger.info("調用買也D額story");
 }
}
package org.worm.biz.springioc;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.worm.biz.Car;
import org.worm.util.LogUtil;
/** 
* @ClassName: TestSpringIoC 
* @Description: TODO 學習瞭解SpringIoC
* @author Administrator
* @date 2016年6月28日 上午10:39:27 
*  
*/  
public class TestSpringIoC {
 private static final Logger logger = LogUtil.getLogger(TestSpringIoC.class);
 /** 
 * @Title: testBeanFactory 
 * @Description: TODO 通過實例認識BeanFactory 
 * @param     設定文件 
 * @return void    返回類型 
 * @throws 
 */ 
 private static void testBeanFactory(){
  ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
  Resource res = resolver.getResource("classpath:beans.xml");
  BeanFactory bf= new XmlBeanFactory(res);
  logger.info("init BeanFactory");
  Car car = bf.getBean("car", Car.class);
  car.introduce();
  System.out.println("car bean is ready for use!");
 }
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  new TestSpringIoC().testBeanFactory();
 }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"  
 xmlns:context="http://www.springframework.org/schema/context"  
 xmlns:p="http://www.springframework.org/schema/p"  
 xmlns:mvc="http://www.springframework.org/schema/mvc"  
 xmlns:aop="http://www.springframework.org/schema/aop"  
 xmlns:tx="http://www.springframework.org/schema/tx"  
 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-4.0.xsd  
      http://www.springframework.org/schema/context  
      http://www.springframework.org/schema/context/spring-context.xsd  
      http://www.springframework.org/schema/mvc  
      http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
      http://www.springframework.org/schema/aop  
      http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
      http://www.springframework.org/schema/tx 
      http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
      <!-- 註解掃描包 -->
      <context:component-scan base-package="org.worm.biz"/>
      
      <!-- 避免IE執行AJAX時,返回JSON出現下載文件 -->
      <bean id="mappingJacksonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
       <property name="supportedMediaTypes">
        <list>
         <value>text/html;charset=UTF-8</value>
        </list>
       </property>
      </bean>
      <!-- 啓動SpringMVC的註解功能,完成請求和註解POJO的映射 -->
      <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
       <property name="messageConverters">
        <list>
         <ref bean="mappingJacksonHttpMessageConverter"/>
        </list>
       </property>
      </bean>
       <!-- 定義跳轉的文件的前後綴 ,視圖模式配置-->  
     <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">  
         <!-- 這裏的配置我的理解是自動給後面action的方法return的字符串加上前綴和後綴,變成一個 可用的url地址 -->  
<!--          <property name="prefix" value="/WEB-INF/jsp/" />   -->
<!--          <property name="suffix" value=".jsp" />   -->
     </bean>  
      
      <!-- 訪問靜態資源 -->
<!--       <mvc:resources location="/p_w_picpaths/" mapping="/p_w_picpaths/**"/> -->
<!--       <mvc:resources location="/css/" mapping="/css/**"/> -->
<!--       <mvc:resources location="/js/" mapping="/js/**"/> -->
      
       
      <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="defaultEncoding" value="utf-8" />
    <!-- 設置文件上傳的最大尺寸 -->
       <property name="maxUploadSize" value="10485760000" />
       <property name="maxInMemorySize" value="40960" />
 </bean>
 
 <bean id="car" class="org.worm.biz.Car" 
  p:brand = "紅旗CA72"
  p:color = "黑色"
  p:maxSpeed = "200"/>
</beans>

運行結果:

wKioL1dx74iSFhgvAACHz6GBRe4791.png-wh_50

通過註解配置beans.xml的類Beans.java

package org.worm.biz;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/** 
* @ClassName: Beans 
* @Description: TODO 基於註解的配置方式,類似於bens.xml
* @author Administrator
* @date 2016年6月28日 上午11:40:29 
*  
*/ 
@Configuration
public class Beans {
 @Bean(name = "car")
 public Car buildCar(){
  Car car = new Car();
  car.setBrand("紅旗CA72_Annotation");
  car.setMaxSpeed(200);
  car.setColor("黑色_Annotation");
  return car;
 }
}

加載ApplicationContext

/** 
 * @Title: testApplicationContextWithAnnotatioin 
 * @Description: TODO 實例演示applicationContext
 * @param     設定文件 
 * @return void    返回類型 
 * @throws 
 */ 
 private static void testApplicationContextWithAnnotatioin(){
  ApplicationContext ctx = new AnnotationConfigApplicationContext(Beans.class);
  Car car = ctx.getBean("car",Car.class);
  car.introduce();
 }

3、資源加載

    爲了訪問不同類型的資源,必須使用相應的Resource實現類,是否可以在不顯示使用Resource實現類的情況下,僅通過資源地址的特殊標識就可以加載相應的資源了呢?Spring提供了一個強大的加載資源的機制,不但能夠通過“classpath”、"file"等資源地址前綴識別不同的資源類型,還支持Ant風格帶通配符的資源地址

地址前綴
示例
對應資源類型
classpath:
classpath:org/aku/worm/biz/beans.xml
從類路徑中加載資源,classpath:和classpath:/是等價的,都是相對於類的根路徑。資源文件可以在標準的文件系統中,可以在jar或者zip的類包中
file:
file:/org/aku/worm/biz/beans.xml
使用UrlResource從文件系統目錄中加載資源,可以採用絕對路徑或相對路徑
http://
http://www.aku.org/resource/beans.xml
使用UrlResource從Web服務器中裝載資源
ftp://
ftp://www.aku.org/resource/beans.xml
使用UrlResource從FTP服務器中加載資源
沒有前綴
org/aku/worm/biz/beans.xml
根據ApplicationContext具體實現類採用對應的Resource
classpath*:
classpath*:org/aku/worm/biz/beans.xml假設多個Jar包或者文件系統類路徑中都擁有一個相同的包名(org.aku.worm)。classpath:只會在第一個加載的org.aku.worm包下查找,而classpath*:會掃描所有這個jar包或者類路徑下的org.aku.worm類路徑。

4、Bean基本配置

   4.1、 在<beans>標籤內裝載bean,bean的id屬性是唯一的,此外id的命名需要滿足xml對id的命名規範:必須以字母開始,後面可以是字母、數字、連字符、下劃線、句號、冒號等完成結束符,逗號和空格等是非法的。

    4.2、依賴注入

        屬性注入,通過setter()方法注入Bean的屬性或依賴對象,由於屬性注入方式具有可選擇性和靈活性高的優點,所以屬性注入是最常用的注入方式。

        構造函數注入,它保證一些必要的屬性在Bean實例化時就得到設置,並確保了Bean實例在實例化後就可以使用。可按照類型匹配入參、聯合使用類型和索引匹配入參和通過自身類型反射匹配入參

    4.3、注入參數詳解

        字面值,一般指可用字符串表示的值,這些值可以通過<value>標籤元素進行注入。在xml中<![CDTAT[]]>的作用是讓XML解析器將標籤中的字符串當做普通文本對待,以防止某些字符串對xml格式造成破壞

        引用其他Bean,通過<ref>元素就可以引用其他Bean,建立起依賴關係

        集合類型屬性,Spring爲集合類型(List、Set、Map和Properties)屬性提供了專門的配置元素標籤。List屬性用<list>。Map屬性用<map><entry><key/><value/></entry></map>來配置。Properties可以看成Map類型的特例。<props><prop key></prop></props>。最後集合屬性可以通過util命名空間配置集合類型的Bean

    4.4、Bean的作用域

        在Spring中Bean有5個作用域,singleton(在springIoC容器中,僅存在一個Bean實例),prototype(每次從容器中調用Bean時,都相當於執行了一個new XXXBean()),request(每次HTTP請求都會創建一個新的Bean),session(同一個HTTP Session共享一個Bean),globalSession(同一個全局Session共享一個Bean)。

        在Spring中推薦採用配置方式“Scope=<作用域類型>”。

    4.5、基於註解的Bean

        Spring容器成功啓動的三大要件分別是:Bean定義信息、Bean實現類以及Spring本身。Spring提供了3個功能分別對於DAO、Service以及Web層的Controller進行註解。

5、總結

    本文分析了IoC的概念,控制反轉其實包含兩層意思,“控制”是接口實現類的選擇控制權,而“反轉”是指這種選擇控制權從調用類轉移到了外部第三方類或容器中。

    BeanFactory、ApplicationContext和WebApplicationContext是Spring框架的3個最核心的接口,框架中其他大部分都是圍繞着他們展開、爲他們提供支持和服務。Spring提供了一個強大的加載資源的機制,不僅能夠通過classpath、file等資源地址前綴識別不同的資源類型,還支持Ant風格帶通配符的資源地址。

    本文還講解了在Spring配置文件中配置Bean的各種知識。




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