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>
運行結果:
通過註解配置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進行註解。
@Repository:用於對DAO實現類進行註解
@Service:用於對Service進行註解
@Controller:用於對Controller進行註解
5、總結
本文分析了IoC的概念,控制反轉其實包含兩層意思,“控制”是接口實現類的選擇控制權,而“反轉”是指這種選擇控制權從調用類轉移到了外部第三方類或容器中。
BeanFactory、ApplicationContext和WebApplicationContext是Spring框架的3個最核心的接口,框架中其他大部分都是圍繞着他們展開、爲他們提供支持和服務。Spring提供了一個強大的加載資源的機制,不僅能夠通過classpath、file等資源地址前綴識別不同的資源類型,還支持Ant風格帶通配符的資源地址。
本文還講解了在Spring配置文件中配置Bean的各種知識。