Spring常見知識點

Spring框架的七大模塊

  • Spring Core:框架的最基礎部分,提供IoC容器,對bean進行管理
  • Spring Context:繼承BeanFactory,提供上下文信息,拓展出JNDI, EJB, 電子郵件,國際化等功能
  • Spring DAO:提供了JDBC的抽象層,還提供了聲明性事務管理方法
  • Spring ORM:提供了JPA, JDO, Hibernate, MyBatis等ORM映射層
  • Spring AOP:集成了所有AOP功能
  • Spring Web:提供了基礎的Web開發的上下文信息,現有的web框架,如JSF, Tapestry, Structs等,提供了集成
  • Spring Web MVC:提供了Web應用de Model-View-Controller全功能實現

Bean定義5種作用域

  1. singleton(單例)
  2. prototype(原型)
  3. request
  4. session
  5. global session

Spring IoC初始化流程

☞ 第一步 Resource定位

Resource是Spring中用於封裝I/O操作的接口。正如前面所見,在創建spring容器時,通常要訪問XML配置文件,除此之外還可以通過訪問文件類型、二進制流等方式訪問資源,還有當需要網絡上的資源時可以通過訪問URL,Spring把這些文件統稱爲Resource,常用的Resource資源類型如下:

  • FileSystemResource:以文件的絕對路徑方式進行訪問資源,效果類似於Java中的File
  • ClassPathResource:以類路徑的方式訪問資源,效果類似於this.getClass().getResource("/).getPath("")
  • ServletContextResource:web應用根目錄的方式訪問資源,效果類似於request.getServletContext().getRealPath("")
  • UrlResource:訪問網絡資源的實現類,如file: http: ftp: 等前綴的資源對象
  • ByteArrayResource:訪問字節數組資源的實現類

Spring提供了ResourceLoader接口用於實現不同的Resource加載策略,該接口的實例對象中可以獲取一個resource對象,也就是說將不同的Resource實例的創建交給ResourceLoader的實現類來處理。

☞ 第二步 通過返回的Resource對象,進行BeanDefinition的載入

1. 什麼是BeanDefinition? BeanDefinition與Resource的聯繫?

官方文檔中對BeanDefinition的解釋如下:

A BeanDefinition describes a bean instance, which has property values, constructor argument values, and further information supplied by concrete implementations.

Load bean definitions from the specified resource.

總之,BeanDefinition相當於一個數據結構,這個數據結構的生成過程是根據定位的Resource資源對象中的bean而來的,這些bean在Spring IoC容器內部表示成了BeanDefinition這樣的數據結構,IoC容器對Bean的管理和依賴注入的實現都是通過操作BeanDefinition來進行的。

2. 如何將BeanDefinition載入到容器?

在Spring中配置文件主要格式是XML,對於用來讀取XML型資源文件來進行初始化的IoC容器而言,該類容器會使用到AbstractXmlApplicationContext類,該類定義了一個名爲loadBeanDefinitions(DefalutListableBeanFactory beanFactory)的方法用來獲取BeanDefinition。

☞ 第三步 將BeanDefinition註冊到容器中

最終Bean配置會被解析成BeanDefinition並與beanNames, Alias以同封裝到BeanDefinitionHolder類中,之後beanFactory.registerBeanDefinition(beanName, bdHolder.gerBeanDefinition())註冊到DefaultListableBeanFactory.beanDefinitionMap中。之後客戶端如果要獲取Bean對象,Spring容器會根據註冊的BeanDefinition進行實例化。

BeanDefinition加載流程

定義BeanDefinitionReader解析xml的document,BeanDefinitionDocumentReader解析document成beanDefinition

DI依賴注入流程(實例化,處理Bean之間的依賴關係)

過程在IoC初始化後,依賴注入的過程是用戶第一次向IoC容器索要Bean時觸發。

  • 如果設置lazy-init=true,會在第一次getBean的時候才初始化Bean,lazy-int=false時,會在容器啓動的時候直接初始化(singleton bean)
  • 調用BeanFactory.getBean()生成bean
  • 生成bean過程運用裝飾器模式產生的bean都是beanWrapper (bean的增強)

☞ 依賴注入如何處理Bean之間的依賴關係

在beanDefinition載入時,如果bean有依賴關係,通過佔位符來代替,在調用getBean時候,如果遇到佔位符,從IoC裏獲取bean注入到本實例來。

Bean的生命週期

  • 實例化Bean:IoC容器通過獲取BeanDefinition對象中的信息進行實例化,實例化對象被包裝在BeanWrapper對象中
  • 設置對象屬性(DI):通過BeanWrapper提供的設置屬性的接口完成屬性依賴注入
  • 注入Aware接口(BeanFactoryAware,可以用這個方法來獲取其它Bean,ApplicationContextAware):Spring會檢測該對象是否實現了xxxAware接口,並將相關xxxAware實例注入給bean
  • BeanPostProcessor:自定義的處理(分前置處理和後置處理)
  • InitializingBean和init-method:執行我們自定義的初始化方法
  • 使用
  • destory:bean的銷燬

Spring的IoC注入方式

  • 構造器注入
  • setter方法注入
  • 註解注入
  • 接口注入

Soring的循環依賴

☞ 什麼是循環依賴?

循環依賴其實就是循環引用,也就是兩個或者兩個以上的bean相互持有對方,最終形成閉環。比如A依賴於B,B依賴於C,C依賴於A,如圖:

這裏寫圖片描述

 Spring中循環依賴場景有:

  1. 構造器的循環依賴
  2. field屬性的循環依賴

☞ 怎麼檢測是否存在循環依賴?

Bean在創建的時候可以給Bean打標,如果遞歸調用回來發現正在創建中的話,即說明存在循環依賴。

☞ 如何解決循環依賴?

Spring的單例對象的初始化主要分爲三步:(1)CreateBeanInstance實例化(調用對象的構造方法實例化對象);(2)populateBean填充屬性(多bean的依賴屬性進行填充);(3)InitializeBean初始化(調用spring xml中的init方法)

從上面單例bean初始化步驟可以看到,循環依賴主要發生在第一、第二部,也就是構造器循環依賴和filed循環依賴。

在Spring容器整個生命週期內,有且只有一個對象,對象應該存在Cache中,Spring爲了解決單例的循環依賴問題,使用了三級緩存

這三個緩存分別指:

  1. singletonObjects:第一級緩存,裏面放置的是實例化好的單例對象;
  2. earlySingletonObjects:第二級緩存,裏面存放的是提前曝光的單例對象
  3. singletonFactories:第三級緩存,裏面存放的是要被實例化的對象的對象工廠

創建bean的時候Spring首先從一級緩存singletonObjects中獲取,如果獲取不到,並且對象正在創建中,就再從二級緩存earlySingletonObjects中獲取,如果還是獲取不到就從三級緩存singletonFactories中取(Bean調用構造函數進行實例化後,即使屬性還爲填充,就可以通過三級緩存向外提前暴露依賴的引用值,根據對象引用能定位到堆中的對象,其原理是基於Java的引用傳遞),取到後從三級緩存移動到二級緩存,完全初始化之後將自己放入到一級緩存中供其他使用。

因爲加入singletonFactories三級緩存的前提是執行了構造器,所以構造器的循環依賴沒法解決。

Spring中使用了哪些設計模式?

  • 工廠模式:spring中的BeanFactory就是簡單工廠模式的體現,根據傳入唯一的標識來獲得bean對象;
  • 單例模式:提供了全局的訪問點BeanFactory
  • 代理模式:AOP功能的原理就是使用了代理模式
  • 裝飾器模式:依賴注入就需要使用BeanWrapper
  • 觀察者模式:Spring中Observer模式常用的地方是listener的實現,如ApplicationListener
  • 策略模式:Bean實例化的時候決定採用何種方式初始化bean實例(反射或者GCLIB動態字節碼生成)

AOP核心概念

  • 切面(aspect):類是對物體特徵的抽象,切面就是對橫切關注點的抽象
  • 橫切關注點:對那些方法進行攔截,攔截後怎麼處理,這些關注點稱之爲橫切關注點
  • 連接點(joinpoint):被攔截到的點,因爲spring只支持方法類型的連接點,所以在Spring中連接點指的就是攔截到的方法,實際上連接點還可以是字段或者構造器
  • 切入點(pointcut):對連接點進行攔截的定義
  • 通知(advice):所謂通知指的就是攔截到連接點之後要執行的代碼,通知分爲前置、後置、異常、最終、環繞通知五類
  • 目標對象:代理的目標對象
  • 織入(weave):將切面應用到目標對象並導致代理對象創建的過程
  • 引入(introduction):在不修改代碼的前提下,引入可以在運行期爲類動態地添加方法或字段

解釋一下AOP

傳統OOP開發代碼邏輯是自上而下地,這個過程中會產生一些橫切性問題,這些問題與我們主業務邏輯關係不大,會散落在代碼地各個地方,造成難以維護,AOP的思想就是把業務邏輯與橫切的問題進行分離,達到解耦的目的,提高代碼重用性和開發效率

AOP主要應用場景

  • 記錄日誌
  • 監控性能
  • 權限控制
  • 事務管理

AOP源碼分析

  • @EnableAspectAutoProxy給容器(beanFactory)中註冊一個AnnotationAwareAspectAutoJProxyCreator對象
  • AnnotationAwareAspectJAutoProxyCreator對目標對象進行代理對象的創建,對象內部,是封裝JDK和CGlib兩個技術,實現動態代理對象的創建(創建代理對象的過程中,會先創建一個代理工廠,獲取到所有的增強器(通知方法),將這些增強器和目標類注入到代理工廠,再用代理工廠創建對象)
  • 代理對象執行目標方法,得到目標方法的攔截器鏈,利用攔截器的鏈式機制,依次進入每一個攔截器進行執行

AOP使用哪種代理對象

  • 當bean的實現中存在接口或者是Proxy的子類,使用JDK動態代理;不存在接口,Spring採用CGLIB來生成代理對象
  • JDK動態代理主要涉及到java.lang.reflect包中的兩個類:Proxy和InvocationHandler
  • Proxy利用InovationHandler(定義橫切邏輯)接口動態創建目標類的代理對象

JDK動態代理

  • 通過bind方法建立代理與真實對象關係,通過Proxy.newProxyInstance(target)生成代理對象
  • 代理對象通過反射invoke方法實現調用真實對象的方法

靜態代理與動態代理區別 

  • 靜態代理:程序運行前代理類的.class文件就存在了
  • 動態代理:在程序運行時利用反射動態創建代理對象(複用性、易用性、更加集中)

CGLIB和JDK動態代理區別

  • JDK動態代理必須提供接口才能使用
  • CGLIB不需要,只要一個非抽象類就能實現動態代理
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章