Spring
前奏
框架是什么?
框架就是一些類和接口的集合,通過這些類和接口協(xié)調(diào)來完成一系列的程序?qū)崿F(xiàn)。
優(yōu)點:
減少重復的代碼的編寫、
讓代碼的結(jié)構(gòu)更加清晰、耦合度更低,開發(fā)后期維護方便.
減少BUG的產(chǎn)生
1 Spring概述
Spring 是最受歡迎的企業(yè)級 Java 應(yīng)用程序開發(fā)框架,數(shù)以百萬的來自世界各地的開發(fā)人員使用 Spring 框架來創(chuàng)建性能好、易于測試、可重用的代碼。
Spring 框架是一個開源的 Java 平臺,它最初是由 Rod Johnson 編寫的,并且于 2003 年 6 月首次在 Apache 2.0 許可下發(fā)布。
Spring 是輕量級的框架,其基礎(chǔ)版本只有 2 MB 左右的大小。
Spring 框架的核心特性是可以用于開發(fā)任何 Java 應(yīng)用程序,但是在 Java EE 平臺上構(gòu)建 web 應(yīng)用程序是需要擴展的。 Spring 框架的目標是使 J2EE 開發(fā)變得更容易使用,通過啟用基于 POJO 編程模型來促進良好的編程實踐。
1.1 Spring介紹
1.Spring是什么?
Spring是一個開源的輕量級的Java開發(fā)框架
2.Spring有什么作用?
簡化應(yīng)用程序的開發(fā)。
3.簡化應(yīng)用程序開發(fā)體現(xiàn)在哪些方面?
①IOC容器
② AOP
1.2 Spring簡介
Spring框架由Rod Johnson開發(fā),2004年發(fā)布了Spring框架的第一版。Spring是一個從實際開發(fā)中抽取出來的框架,因此它完成了大量開發(fā)中的通用步驟,留給開發(fā)者的僅僅是與特定應(yīng)用相關(guān)的部分,從而大大提高了企業(yè)應(yīng)用的開發(fā)效率。
Spring總結(jié)起來優(yōu)點如下:
一、低侵入式設(shè)計,代碼的污染極低。
二、獨立于各種應(yīng)用服務(wù)器,基于Spring框架的應(yīng)用,可以真正實現(xiàn)Write Once,Run Anywhere的承諾。
三、Spring的IoC容器降低了業(yè)務(wù)對象替換的復雜性,提高了組件之間的解耦。
四、Spring的AOP支持允許將一些通用任務(wù)如安全、事務(wù)、日志等進行集中式管理,從而提供了更好的復用。
五、Spring的ORM和DAO提供了與第三方持久層框架的良好整合,并簡化了底層的數(shù)據(jù)庫訪問。
六、Spring的高度開放性,并不強制應(yīng)用完全依賴于Spring,開發(fā)者可自由選用Spring框架的部分或全部。
Spring 是一個開源框架
Spring 是一個 IOC(DI) 和 AOP 容器框架
2 Spring 體系結(jié)構(gòu)
Spring 有可能成為所有企業(yè)應(yīng)用程序的一站式服務(wù)點,然而,Spring 是模塊化的,允許你挑選和選擇適用于你的模塊,不必要把剩余部分也引入。下面的部分對在 Spring 框架中所有可用的模塊給出了詳細的介紹。
Spring 框架提供約 20 個模塊,可以根據(jù)應(yīng)用程序的要求來使用。

2.1 Spring模塊講解
核心容器由spring-core,spring-beans,spring-context,spring-context-support和spring-expression(SpEL,Spring表達式語言,Spring Expression Language)等模塊組成,它們的細節(jié)如下:
spring-core模塊提供了框架的基本組成部分,包括 IoC 和依賴注入功能。
spring-beans 模塊提供 BeanFactory,工廠模式的微妙實現(xiàn),它移除了編碼式單例的需要,并且可以把配置和依賴從實際編碼邏輯中解耦。
context模塊建立在由core和 beans 模塊的基礎(chǔ)上建立起來的,它以一種類似于JNDI注冊的方式訪問對象。Context模塊繼承自Bean模塊,并且添加了國際化(比如,使用資源束)、事件傳播、資源加載和透明地創(chuàng)建上下文(比如,通過Servelet容器)等功能。Context模塊也支持Java EE的功能,比如EJB、JMX和遠程調(diào)用等。ApplicationContext接口是Context模塊的焦點。spring-context-support提供了對第三方庫集成到Spring上下文的支持,比如緩存(EhCache, Guava, JCache)、郵件(JavaMail)、調(diào)度(CommonJ, Quartz)、模板引擎(FreeMarker, JasperReports, Velocity)等。
spring-expression模塊提供了強大的表達式語言,用于在運行時查詢和操作對象圖。它是JSP2.1規(guī)范中定義的統(tǒng)一表達式語言的擴展,支持set和get屬性值、屬性賦值、方法調(diào)用、訪問數(shù)組集合及索引的內(nèi)容、邏輯算術(shù)運算、命名變量、通過名字從Spring IoC容器檢索對象,還支持列表的投影、選擇以及聚合等。
2.1.1 數(shù)據(jù)訪問/集成
數(shù)據(jù)訪問/集成層包括 JDBC,ORM,OXM,JMS 和事務(wù)處理模塊,它們的細節(jié)如下:
(注:JDBC=Java Data Base Connectivity,ORM=Object Relational Mapping,OXM=Object XML Mapping,JMS=Java Message Service)
JDBC 模塊提供了JDBC抽象層,它消除了冗長的JDBC編碼和對數(shù)據(jù)庫供應(yīng)商特定錯誤代碼的解析。
ORM 模塊提供了對流行的對象關(guān)系映射API的集成,包括JPA、JDO和Hibernate等。通過此模塊可以讓這些ORM框架和spring的其它功能整合,比如前面提及的事務(wù)管理。
OXM 模塊提供了對OXM實現(xiàn)的支持,比如JAXB、Castor、XML Beans、JiBX、XStream等。
JMS 模塊包含生產(chǎn)(produce)和消費(consume)消息的功能。從Spring 4.1開始,集成了spring-messaging模塊。。
事務(wù)模塊為實現(xiàn)特殊接口類及所有的 POJO 支持編程式和聲明式事務(wù)管理。(注:編程式事務(wù)需要自己寫beginTransaction()、commit()、rollback()等事務(wù)管理方法,聲明式事務(wù)是通過注解或配置由spring自動處理,編程式事務(wù)粒度更細)
2.1.2 Web
Web 層由 Web,Web-MVC,Web-Socket 和 Web-Portlet 組成,它們的細節(jié)如下:
Web 模塊提供面向web的基本功能和面向web的應(yīng)用上下文,比如多部分(multipart)文件上傳功能、使用Servlet監(jiān)聽器初始化IoC容器等。它還包括HTTP客戶端以及Spring遠程調(diào)用中與web相關(guān)的部分。。
Web-MVC 模塊為web應(yīng)用提供了模型視圖控制(MVC)和REST Web服務(wù)的實現(xiàn)。Spring的MVC框架可以使領(lǐng)域模型代碼和web表單完全地分離,且可以與Spring框架的其它所有功能進行集成。
Web-Socket 模塊為 WebSocket-based 提供了支持,而且在 web 應(yīng)用程序中提供了客戶端和服務(wù)器端之間通信的兩種方式。
Web-Portlet 模塊提供了用于Portlet環(huán)境的MVC實現(xiàn),并反映了spring-webmvc模塊的功能。
2.1.3 其他
還有其他一些重要的模塊,像 AOP,Aspects,Instrumentation,Web 和測試模塊,它們的細節(jié)如下:
AOP 模塊提供了面向方面的編程實現(xiàn),允許你定義方法攔截器和切入點對代碼進行干凈地解耦,從而使實現(xiàn)功能的代碼徹底的解耦出來。使用源碼級的元數(shù)據(jù),可以用類似于.Net屬性的方式合并行為信息到代碼中。
Aspects 模塊提供了與 AspectJ 的集成,這是一個功能強大且成熟的面向切面編程(AOP)框架。
Instrumentation 模塊在一定的應(yīng)用服務(wù)器中提供了類 instrumentation 的支持和類加載器的實現(xiàn)。
Messaging 模塊為 STOMP 提供了支持作為在應(yīng)用程序中 WebSocket 子協(xié)議的使用。它也支持一個注解編程模型,它是為了選路和處理來自 WebSocket 客戶端的 STOMP 信息。
測試模塊支持對具有 JUnit 或 TestNG 框架的 Spring 組件的測試。
3 Spring常用特性
利用Spring來創(chuàng)建對象(JavaBean工廠) 利用Spring構(gòu)建業(yè)務(wù)邏輯層 管理依賴關(guān)系 適應(yīng)需求變更 利用Spring創(chuàng)建數(shù)據(jù)訪問對象(DAO) 利用Spring進行事務(wù)處理、日志操作
4 Spring安裝與下載
[Spring官網(wǎng)] https://spring.io/ Spring官網(wǎng)
1 下載并解壓
http://repo.spring.io/release/org/springframework/spring/
下載zip壓縮包: spring-framework-4.0.6.RELEASE-dist.zip 并解壓。
2 創(chuàng)建普通Java Project
eclipse or
[idea] https://www.cnblogs.com/yangyquin/p/5285272.html IDEA
3 將相應(yīng)的jar包加入類路徑
spring.jar
當然,這些jar包對于一個web項目還是不夠的,仍需要一些其他的jar包,如commons-lang-.jar,commons-fileupload-.jar等等。
使用Spring框架開發(fā)最少需要:
commons-logging.jar
spring-aop.jar
spring-core.jar
spring-context.jar
spring-beans.jar
spring-expression.jar
spring-web.jar
spring-tx.jar
aopalliance.jar
aspectjrt.jar aspectjweaver.jar
4創(chuàng)建applicationContext.xml 配置文件
在src目錄下創(chuàng)建文件 applicationContext.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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
</beans>
xmlns可到此目錄引用 命名空間(或通過直接URL方式): Spring4.8Jar/spring-framework-4.3.8.RELEASE/docs/spring-framework-reference/html/xsd-configuration.html
5 Spring IOC
Spring兩大核心技術(shù):
控制反轉(zhuǎn)(IOC)和面向切面編程(AOP)的輕量級的容器,為軟件開發(fā)提供全方位支持的應(yīng)用程序框架。
容器是符合某種規(guī)范能夠提供一系列服務(wù)的管理器,開發(fā)人員可以利用容器所提供的服務(wù)來方便地實現(xiàn)某些特殊的功能。 所謂的“重量級”容器是指那些完全遵守J2EE的規(guī)范,提供規(guī)范中所有的服務(wù)。 “輕量級”容器的也是遵守J2EE的規(guī)范,但其中的服務(wù)可以自由配置。
Spring IOC簡介:
控制反轉(zhuǎn)(IOC) (Inversion of Control)
依賴注入(DI) (Dependency Injection)
當某個角色(比如創(chuàng)建一個JAVA實例,調(diào)用者)需要另一個角色(另一個JAVA實例,被調(diào)用者)的協(xié)助時,在傳統(tǒng)的程序設(shè)計過程中,通常由調(diào)用者來創(chuàng)建被調(diào)用者的實例。但在Spring里,創(chuàng)建被調(diào)用者的工作不再由調(diào)用者來完成,因此稱為控制反轉(zhuǎn);創(chuàng)建被調(diào)用者實例的工作通常由Spring容器來完成,然后注入調(diào)用者,因此也稱為依賴注入。
Spring核心容器是整個應(yīng)用中的超級大工廠,所有的JAVA對象都交給Spring容器管理—這些JAVA對象被統(tǒng)稱為Spring容器中的bean
控制反轉(zhuǎn)示例
applicationContext.xml
在 Spring 的 IOC 容器里配置 Bean
在 xml 文件中通過 bean 節(jié)點來配置 bean
<bean id="user" class="com.qfjy.bean.User"></bean>
id:bean 的名稱。
在 IOC 容器中必須是唯一的 id 可以指定多個名字,名字之間可用逗號、分號、或空格分隔
每個bean都會指定class屬性,class屬性指明了Bean的來源,即Bean的實際路徑,注意,這里要寫出完整的包名+類名(全限定名)。
java類
public class DemoTest {
public static void main(String[] args) {
/**
* 調(diào)用方創(chuàng)建對象
*/
User user=new User();
user.setId(123);
user.setName("qfjy");
System.out.println(user);
/**
* Spring IOC容器管理對象創(chuàng)建
*/
//創(chuàng)建Spring IOC容器
ApplicationContext ctx=
new ClassPathXmlApplicationContext("applicationContext.xml");
//在容器中取出對應(yīng)bean實例
User user1= (User) ctx.getBean("user");
System.out.println(user1);
}
}
應(yīng)用本身不負責依賴對象的創(chuàng)建和維護,而是由外部容器來負責。
這樣控制權(quán)就由應(yīng)用轉(zhuǎn)移到外部容器,控制權(quán)的轉(zhuǎn)移就是所謂的反轉(zhuǎn)。
Bean其它描述
在Bean中有一個id屬性及class屬性,這個id唯一標識了該Bean。在配置文件中,不能有重復的Bean的id, 因為在代碼中通過BeanFactory或ApplicationContext來獲取Bean的實例時,都要用它來作為唯一索引。
Bean的id屬性 為Bean指定別名可以用name屬性來完成,如果需要為Bean指定多個別名, 可以在name屬性中使用逗號(,)、分號(;)或空格來分隔多個別名,在程序中可以通過任意一個別名訪問該Bean實例。 例如,id為“HelloWorld”的Bean,其別名為:
<bean id="user" name="u;u1;u2" class="com.qfjy.bean.User"></bean>
則在程序中可以利用“u”、“u1”、“u2”任意一個來獲取Bean的實例
以下三種方法均可完成Bean實例的獲取
//根據(jù)名稱在容器中取出對應(yīng)bean實例
User u= (User) ctx.getBean("u");
User u1= (User) ctx.getBean("u1");
User u2= (User) ctx.getBean("u2");
BeanFactory
簡介:
Spring容器最基本的接口就是BeanFactor。BeanFactory負責配置、創(chuàng)建、管理Bean,他有一個子接口:ApplicationContext,因此也稱之為Spring上下文。Spring容器負責管理Bean與Bean之間的依賴關(guān)系。
BeanFactory:是IOC容器的核心接口, 它定義了IOC的基本功能,我們看到它主要定義了getBean方法。getBean方法是IOC容器獲取bean對象和引發(fā)依賴注入的起點。方法的功能是返回特定的名稱的Bean。
BeanFactory 是初始化 Bean 和調(diào)用它們生命周期方法的“吃苦耐勞者”。注意,BeanFactory 只能管理單例(Singleton)Bean 的生命周期。它不能管理原型(prototype,非單例)Bean 的生命周期。這是因為原型 Bean 實例被創(chuàng)建之后便被傳給了客戶端,容器失去了對它們的引用。
BeanFactory有著龐大的繼承、實現(xiàn)體系,有眾多的子接口、實現(xiàn)類。
BeanFactory接口包含以下幾個基本方法:
? Boolean containBean(String name):判斷Spring容器是否包含id為name的Bean實例。
? <T> getBean(Class<T> requiredTypr):獲取Spring容器中屬于requiredType類型的唯一的Bean實例。
? Object getBean(String name):返回Sprin容器中id為name的Bean實例。
BeanFactorty接口提供了配置框架及基本功能,但是無法支持spring的aop功能和web應(yīng)用。而ApplicationContext接口作為BeanFactory的派生,因而提供BeanFactory所有的功能。而且ApplicationContext還在功能上做了擴展,相較于BeanFactorty,ApplicationContext還提供了以下的功能:
(1)MessageSource, 提供國際化的消息訪問 (2)資源訪問,如URL和文件 (3)事件傳播特性,即支持aop特性 (4)載入多個(有繼承關(guān)系)上下文 ,使得每一個上下文都專注于一個特定的層次,比如應(yīng)用的web層
ApplicationContext:是IOC容器另一個重要接口, 它繼承了BeanFactory的基本功能, 同時也繼承了容器的高級功能,如:MessageSource(國際化資源接口)、ResourceLoader(資源加載接口ApplicationEventPublisher(應(yīng)用事件發(fā)布接口)等。
二者區(qū)別
BeanFactroy采用的是延遲加載形式來注入Bean的,即只有在使用到某個Bean時(調(diào)用getBean()),才對該Bean進行加載實例化,這樣,我們就不能發(fā)現(xiàn)一些存在的Spring的配置問題。而ApplicationContext則相反,它是在容器啟動時,一次性創(chuàng)建了所有的Bean。ApplicationContext 唯一的不足是占用內(nèi)存空間。當應(yīng)用程序配置Bean較多時,程序啟動較慢。
BeanFactory負責讀取bean配置文檔,管理bean的加載,實例化,維護bean之間的依賴關(guān)系,負責bean的聲明周期。ApplicationContext除了提供上述BeanFactory所能提供的功能之外,還提供了更完整的框架功能(國際化..資源訪問..etc..)
常用的獲取ApplicationContext的方式:FileSystemXmlApplicationContext:從文件系統(tǒng)或者url指定的xml配置文件創(chuàng)建,參數(shù)為配置文件名或文件名數(shù)組,有相對路徑與絕對路徑。
ApplicationContext factory=new FileSystemXmlApplicationContext("src/applicationContext.xml");
ApplicationContext factory=new FileSystemXmlApplicationContext("D:/java/eclipse/workSpace/1-spring/src/applicationContext.xml");
4.ClassPathXmlApplicationContext:從classpath的xml配置文件創(chuàng)建,可以從jar包中讀取配置文件。ClassPathXmlApplicationContext 編譯路徑總有幾種方式:
ApplicationContext factory = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
ApplicationContext factory = new ClassPathXmlApplicationContext("applicationContext.xml");
依賴注入示例
Spring 支持 3 種依賴注入的方式
屬性注入 構(gòu)造器注入 工廠方法注入(很少使用,不推薦)
屬性注入
Set注入 最簡單的注入方式(簡單、直觀、Spring大量使用)
<bean id="user" name="u;u1;u2" class="com.qfjy.bean.User">
<!--Set注入 -->
<property name="id" value="1"></property>
<property name="name" value="qfjy"></property>
</bean></pre>
構(gòu)造器注入
是指帶有參數(shù)的構(gòu)造函數(shù)注入(完成程序初始化動作)
<bean id="user" class="com.qfjy.bean.User">
<!--構(gòu)造器注入,需添加對應(yīng)的構(gòu)造方法 -->
<constructor-arg name="id" value="99"></constructor-arg>
<constructor-arg name="name" value="qfjy"></constructor-arg>
</bean></pre>
1、根據(jù)參數(shù)的名字傳值:(推薦用法)
<constructor-arg name="id" value="99"></constructor-arg></pre>
2、直接傳值。這種方法也是根據(jù)順序排的,所以一旦調(diào)換位置的話,就會出現(xiàn)bug
<bean id="user" class="com.qfjy.bean.User">
<!--構(gòu)造器注入,需添加對應(yīng)的構(gòu)造方法 -->
<constructor-arg value="99"></constructor-arg>
<constructor-arg value="qfjy"></constructor-arg>
</bean></pre>
3、根據(jù)索引賦值 index,索引都是以0開頭
<bean id="user" class="com.qfjy.bean.User">
<constructor-arg index="0" value="99"></constructor-arg>
<constructor-arg index="1" value="qfjy"></constructor-arg>
</bean></pre>
注入其它類型
JavaBean對象
<bean id="user" class="com.qfjy.bean.User">
<property name="name" value="qfjy"></property>
<property name="role" ref="role"></property>
</bean>
<bean id="role" class="com.qfjy.bean.Role">
<property name="rname" value="管理員"></property>
</bean>
</pre>
內(nèi)部Bean
當 Bean 實例僅僅給一個特定的屬性使用時, 可以將其聲明為內(nèi)部 Bean.
內(nèi)部 Bean 聲明直接包含在 <property> 或 <constructor-arg> 元素里, 不需要設(shè)置任何 id 或 name 屬性 內(nèi)部 Bean 不能使用在任何其他地方
<bean id="user" class="com.qfjy.bean.User">
<property name="name" value="qfjy"></property>
<!-- 內(nèi)部bean 特點:不能被外部bean訪問-->
<property name="role" >
<bean class="com.qfjy.bean.Role">
<property name="rname" value="管理員"></property>
</bean>
</property>
</bean></pre>
集合類型List
對list集合的注入。如果類中有l(wèi)ist類型的屬性,在為其依賴注入值的時候就需要在配置文件中的<property>元素下應(yīng)用其子元素<list>。
<property name="infos">
<list>
<value>喜歡學習java</value>
<value>唱歌聽音樂</value>
</list>
</property></pre>
集合類型注入bean
<property name="roles">
<list>
<!--引用外部bean -->
<ref bean="role"></ref>
<!--內(nèi)部bean -->
<bean class="com.qfjy.bean.Role">
<property name="rid" value="1"></property>
<property name="rname" value="管理員"></property>
</bean>
</list>
</property></pre>
集合類型Map
<property name="map">
<map key-type="java.lang.String" value-type="com.qfjy.bean.Role">
<entry key="r1" value-ref="role"></entry>
<entry key="r2" >
<bean class="com.qfjy.bean.Role">
<property name="rid" value="1"></property>
<property name="rname" value="管理員"></property>
</bean>
</entry>
</map>
</property></pre>
6 Bean作用域
創(chuàng)建一個bean定義,其實質(zhì)是用該bean定義對應(yīng)的類來創(chuàng)建實例的對象。
默認情況下, Spring 只為每個在 IOC 容器里聲明的 Bean 創(chuàng)建唯一一個實例, 整個 IOC 容器范圍內(nèi)都能共享該實例:所有后續(xù)的 getBean() 調(diào)用和 Bean 引用都將返回這個唯一的 Bean 實例. 該作用域被稱為 singleton, 它是所有 Bean 的默認作用域.
五種作用域中,request、session和global session三種作用域僅在基于web的應(yīng)用中使用(不必關(guān)心你所采用的是什么web應(yīng)用框架),只能用在基于web的Spring ApplicationContext環(huán)境。
備注:Portlet是基于Java的Web組件,由Portlet容器管理,并由容器處理請求,生產(chǎn)動態(tài)內(nèi)容。
7 Bean生命周期
Spring IOC 容器管理 Bean 的生命周期, Spring 允許在 Bean 生命周期的特定點執(zhí)行定制的任務(wù).
Spring IOC 容器對 Bean 的生命周期進行管理的過程:
1通過構(gòu)造器或工廠方法創(chuàng)建 Bean 實例 2為 Bean 的屬性設(shè)置值和對其他 Bean 的引用 3調(diào)用 Bean 的初始化方法 4Bean 可以使用了 5當容器關(guān)閉時 close(), 調(diào)用 Bean 的銷毀方法 在 Bean 的聲明里設(shè)置 init-method 和 destroy-method 屬性, 為 Bean 指定初始化和銷毀方法.
<bean id="user" class="com.qfjy.bean.User" init-method="init" destroy-method="destroy"></pre>
public void init(){
System.out.println("----初始化操作---");
}
public void destroy(){
System.out.println("----結(jié)束等操作");
}</pre>
注解方式: init-method:@PostConstruct destroy-method:@PreDestroy
8 Bean 后置處理器
Bean 后置處理器允許在調(diào)用初始化方法前后對 Bean 進行額外的處理。 Bean 后置處理器對 IOC 容器里的所有 Bean 實例逐一處理, 而非單一實例. 其典型應(yīng)用是: 檢查 Bean 屬性的正確性或根據(jù)特定的標準更改 Bean 的屬性.(一個好的框架必備的特性至少得有開閉原則,可擴展性)
一、對Bean 后置處理器而言, 需要實現(xiàn)BeanFactoryPostProcessor 接口. 在初始化方法被調(diào)用前后, Spring 將把每個 Bean 實例分別傳遞給上述接口的以下兩個方法:
BeanPostProcessor接口的源碼,它定義了兩個方法,一個在bean初始化之前,一個在bean初始化之后
public class UserBeanPostProcessor implements BeanPostProcessor {
/**
*
* @param o bean對象
* @param s bean 名稱
* @return
* @throws BeansException
*/
@Override
public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
System.out.println("-->"+o);
System.out.println("-->"+s);
return o;
}
@Override
public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
System.out.println(o);
System.out.println(s);
return o;
}
}</pre>
9 循環(huán)依賴
什么是循環(huán)依賴?
循環(huán)依賴其實就是循環(huán)引用,也就是兩個或者兩個以上的bean互相持有對方,最終形成閉環(huán)。比如A依賴于B,B依賴于A,
注意,這里不是函數(shù)的循環(huán)調(diào)用,是對象的相互依賴關(guān)系。循環(huán)調(diào)用其實就是一個死循環(huán),除非有終結(jié)條件。
Spring中循環(huán)依賴場景有:
(1)構(gòu)造器的循環(huán)依賴 (2)field屬性的循環(huán)依賴 其中,構(gòu)造器的循環(huán)依賴問題無法解決,只能拋出BeanCurrentlyInCreationException異常,在解決屬性循環(huán)依賴時,spring采用的是提前暴露對象的方法。
Spring怎么解決循環(huán)依賴
Spring的循環(huán)依賴的理論依據(jù)基于Java的引用傳遞,當獲得對象的引用時,對象的屬性是可以延后設(shè)置的。(但是構(gòu)造器必須是在獲取引用之前)
(1)createBeanInstance:實例化,其實也就是調(diào)用對象的構(gòu)造方法實例化對象
(2)populateBean:填充屬性,這一步主要是多bean的依賴屬性進行填充
(3)initializeBean:調(diào)用spring xml中的init 方法。
從上面單例bean的初始化可以知道:循環(huán)依賴主要發(fā)生在第一、二步,也就是構(gòu)造器循環(huán)依賴和field循環(huán)依賴。那么我們要解決循環(huán)引用也應(yīng)該從初始化過程著手,對于單例來說,在Spring容器整個生命周期內(nèi),有且只有一個對象,所以很容易想到這個對象應(yīng)該存在Cache中,Spring為了解決單例的循環(huán)依賴問題,使用了三級緩存。
這三級緩存分別指:
singletonFactories : 單例對象工廠的cache
earlySingletonObjects :提前暴光的單例對象的Cache
singletonObjects:單例對象的cache
基于setter屬性的循環(huán)依賴
Spring先是用構(gòu)造實例化Bean對象 ,創(chuàng)建成功后,Spring會通過以下代碼提前將對象暴露出來,此時的對象A還沒有完成屬性注入,屬于早期對象,此時Spring會將這個實例化結(jié)束的對象放到一個Map中,并且Spring提供了獲取這個未設(shè)置屬性的實例化對象引用的方法。 結(jié)合我們的實例來看,當Spring實例化了A、B、C后,緊接著會去設(shè)置對象的屬性,此時A依賴B,就會去Map中取出存在里面的單例B對象,以此類推,不會出來循環(huán)的問題
代碼: applicationContext.xml
A依賴于B B依賴于C
<bean id="a" class="com.qfjy.bean.A">
<property name="name" value="aaaa"></property>
<property name="b" ref="b"></property>
</bean>
<bean id="b" class="com.qfjy.bean.B">
<property name="name" value="bbbb"></property>
<property name="a" ref="a"></property>
</bean></pre>
控制臺打?。?/p>
A.....
B.....
B name.....
A name.....
基于構(gòu)造器的循環(huán)依賴
<bean id="a" class="com.qfjy.bean.A">
<property name="name" value="aaaa"></property>
<constructor-arg index="0" value="aaa"></constructor-arg>
<constructor-arg index="1" ref="b"></constructor-arg>
</bean>
Spring容器會將每一個正在創(chuàng)建的Bean 標識符放在一個“當前創(chuàng)建Bean池”中,Bean標識符在創(chuàng)建過程中將一直保持在這個池中,因此如果在創(chuàng)建Bean過程中發(fā)現(xiàn)自己已經(jīng)在“當前創(chuàng)建Bean池”里時將拋出BeanCurrentlyInCreationException異常表示循環(huán)依賴;在池中的Bean都是未初始化完的,所以會依賴錯誤 ,(初始化完的Bean會從池中移除)
總結(jié):
不要使用基于構(gòu)造函數(shù)的依賴注入,可以通過以下方式解決:
1.在字段上使用@Autowired注解,讓Spring決定在合適的時機注入
2.用基于setter方法的依賴注入。