Spring框架

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)用程序的要求來使用。

image

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模塊建立在由corebeans 模塊的基礎(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 并解壓。

image

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

簡介:

  1. Spring容器最基本的接口就是BeanFactor。BeanFactory負責配置、創(chuàng)建、管理Bean,他有一個子接口:ApplicationContext,因此也稱之為Spring上下文。Spring容器負責管理Bean與Bean之間的依賴關(guān)系。

  2. BeanFactory:是IOC容器的核心接口, 它定義了IOC的基本功能,我們看到它主要定義了getBean方法。getBean方法是IOC容器獲取bean對象和引發(fā)依賴注入的起點。方法的功能是返回特定的名稱的Bean。

  3. BeanFactory 是初始化 Bean 和調(diào)用它們生命周期方法的“吃苦耐勞者”。注意,BeanFactory 只能管理單例(Singleton)Bean 的生命周期。它不能管理原型(prototype,非單例)Bean 的生命周期。這是因為原型 Bean 實例被創(chuàng)建之后便被傳給了客戶端,容器失去了對它們的引用。

  4. 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ū)別

  1. BeanFactroy采用的是延遲加載形式來注入Bean的,即只有在使用到某個Bean時(調(diào)用getBean()),才對該Bean進行加載實例化,這樣,我們就不能發(fā)現(xiàn)一些存在的Spring的配置問題。而ApplicationContext則相反,它是在容器啟動時,一次性創(chuàng)建了所有的Bean。ApplicationContext 唯一的不足是占用內(nèi)存空間。當應(yīng)用程序配置Bean較多時,程序啟動較慢。

  2. BeanFactory負責讀取bean配置文檔,管理bean的加載,實例化,維護bean之間的依賴關(guān)系,負責bean的聲明周期。ApplicationContext除了提供上述BeanFactory所能提供的功能之外,還提供了更完整的框架功能(國際化..資源訪問..etc..)

  3. 常用的獲取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 的默認作用域.

image

五種作用域中,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)造器必須是在獲取引用之前)

image

(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)依賴

image

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方法的依賴注入。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容