javascript
Spring IOC 组件概述
IOC: Inversion of Control(控制反轉), 這里其實指的是: 將程序中需要使用的 POJOs, 丟入到容器中, 解析成統一的 BeanDefinition(主要基于XML的 GenericBeanDefinition/RootBeanDefinition 與 通過 注解生成的 ScannedGenericBeanDefinition), 而 Bean 依賴的信息都在XML中描述(或通過注解描述), 容器則負責 Bean 的統一生成(本來Bean的創建/銷毀是由我們開發人員控制的), 因此造成了 Bean 的創建/銷毀由Bean容器控制的反轉現象!(通過下圖理解一下)
圖中角色:
先看一下 BeanDefinition 主要屬性
PS: 屬性好多, 一直其實上面屬性中相對重要的有一下幾個
-
2.1 class(該實例化的 Bean), name(bean的命名)
-
2.2 scope(定義Bean的生命周期)
-
2.3 constructor arguments(Bean構造函數依賴注入的屬性)
-
2.4 properties(Bean依賴注入的屬性, 這里的 properties 主要還是指XML或properties里面描述的信息)
-
2.5 autowiring mode(是否自動裝配, 若是的話, 則容器會在整個容器中尋找所有符合條件的 Field)
-
2.6 lazy-initialization mode (是否 lazy 生成Bean, 指是否在正真需要使用 Bean時才會創建bean, 但現在運用中一般都是使用 ApplicationContext 的子類, 這個類會在 refresh()
方法里面中調用 finishBeanFactoryInitialization(), 然后再調用
preInstantiateSingletons 來實例化容器中的所有類), -
2.7 initialization method
(Spring 容器中 bean 的初始化方法, 這個完全可以通過實現接口 InitializingBean 來實現) -
2.8 destruction method (Spring 容器中 bean 的銷毀方法, 這個也完全可以通過實現接口 DisposableBean 來實現)
-
PS: 這個有個漏掉的點: 在Spring 創建 Bean的過程中, 容器里面的 BeanFactoryPostProcessor
可以對Bean的元數據進行一定的修改
BeanDefinition 其實是根據 BeanDefinition 的收集策略來進行分類的, 主要分成下面:
先看一下接口BeanDefinitionReader 的主要方法
// beanDefinition 的讀取者 // 這里有個注意點, BeanDefinition 的讀取器不一定非要實現BeanDefinitionReader接口, 其實這里指的就是 AnnotatedBeanDefinitionReader 與 ClassPathBeanDefinitionScanner public interface BeanDefinitionReader {// 這里的 BeanDefinitionRegistry 其實就是 beanFactory 的注冊接口, BeanDefinition 通過這個接口注入到 BeanFactory 看一下 BeanDefinitionRegistry 的實現類就知道了BeanDefinitionRegistry getRegistry();// ResourceLoader 代表的是 BeanDefinitionReader 需要讀取的是 描述BeanDefinition的資源文件加載器, 以前 BeanFactory 與 ResourceLoader 是分離開的兩個組件, 后來出現了 ApplicationContext, 將 beanFactory 與 ResourceLoader 結合到一個類中ResourceLoader getResourceLoader();// 加載類的 ClassLoaderClassLoader getBeanClassLoader();// bean 的命名生成器BeanNameGenerator getBeanNameGenerator();// 加載指定目錄Resource下 BeanDefinition 的方法int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;// 加載多個指定目錄Resource下 BeanDefinition 的方法int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException;// 加載指定目錄Resource下 BeanDefinition 的方法int loadBeanDefinitions(String location) throws BeanDefinitionStoreException;// 加載多個指定目錄Resource下 BeanDefinition 的方法int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException; }上面方法定義了獲取 BeanDefinition的主要生成策略, 但其中有個重要的點, 就是 BeanDefinition 的讀取器不一定非要實現 BeanDefinitionReader 接口, 比如 AnnotatedBeanDefinitionReader(直接注入帶注解的bean) 與 ClassPathBeanDefinitionScanner(掃描指定目錄下面帶注解的Bean)
BeanDefinitionReader 首先是通過一個抽象類 AbstractBeanDefinitionReader 來定義了加載 beanDefinition 的模板方法(模板模式), 接下來根據在不同文件(XML/Properties)中定義Bean描述信息來實現不同類別的 BeanDefinitionReader(策略模式)
AnnotationConfigApplicationContext 時使用的
AnnotatedBeanDefinitionReader, ClassPathBeanBeanDefinitionScanner
是比較重要的, 也是最常見的
Resource: Spring 里面對資源進行封裝的統一抽象接口, 里面定義了一些通用的方法, 詳情如下:
上面是資源抽象的統一接口, 而針對資源獲取的不同方式, 對應的有相應的子類(PS: AbstractResource 是其對應的抽閑子類, 其間定義了一些抽象的方法, 所有的子類都是從這個類派生出來)
在 Spring 中針對獲取資源的不同渠道, 創建了相應的 Resource(主要是依據 路徑的格式不同, 來創建不同類別的 Resource), 其中有我們進行開發中常用的一些類, 如下:
PS: 在上面創建 Resource時, 若遇到 Classpath*: 開頭時, 則將通過 PathMatchingResourcePatternResolver 來進行獲取對應資源, 這時又會判斷是否路徑中含有 “*/?” 這種字符串, 則獲取能正則匹配成功的所有文件;
補充: classpath*: 代表的是獲取當前ClassLoader對應的 URLClassPath下面的所有包中匹配的資源(包括父ClassLoader, 詳情見PathMatchingResourcePatternResolver.getResources(String locationPattern) ); 而 classpath 則是通過Class.getResourceAsStream(String path) 來獲取一個數據流, 對滴就一個文件(詳情見 ClassPathResource.getInputStream())
ResourcceLoader 中定義了獲取Spring中統一資源的方法, 它將根據路徑的前綴來生成對應的 Resource, 其中最通用的就是下面這幾種:
Resource(根據不同的子類類別創建不同的 Resource, 如
FileSystemXmlApplicationContext的FileSystemResource,
GenericWebApplicationContext 的 ServletContextResource,
ClassPathXmlWebApplicationContext的ClassPathResource), 若路徑以
“classpath:” 開頭, 則創建 ClassPathResource, 若不是以上兩種情況, 則直接創建 UrlResource
"classpath*: " 開頭是就通過這個類來獲取 ClassLoader的classpath下面的所有 jar
中的符合條件的XML資源(PS: PathMatchingResourcePatternResolver是一個非常重要的類,
而且它獲取資源的方式非常直接借鑒)
IOC 主要組件 BeanDefinitionDocumentReader
這個類存在的意義主要是封裝成 XML文件為 Document, 并且根據每個節點的命名空間獲取對應的 Namespacehandler 來進行處理解析對應的節點; 其默認實現就是 DefaultBeanDefinitionDocumentReader; 對于普通的命名空間 “http://www.springframework.org/schema/beans”, 則直接用 BeanDefinitionParserDelegate的parseBeanDefinitionElement來進行解析; 若遇到其他命名空間的, 則通過NamespaceHandlerResolver 來獲取對應的 NamespaceHandler 來進行解析
(PS: NamespaceHandler 也是通過META-INF/Spring.handlers 下面定義的命名空間對應的Namespacehandler, 并通過反射來進行創建, 具體看 DefaultNamespacehandlerResolver 的gethandlerMappings )
IOC 主要組件 NamespaceHandler
Spring解析XML 時, 主要依據命名空間來選擇對應的 NamespaceHandler 來進行解析
NamespaceHandler 來進行解析(比如遇到 <mvc…>, <aop…>, <tx…>, 或者 dubbo
中的)
DefaultNamespaceHandlerResolver 通過獲取所有 “META-INF/spring.handlers” 里面定義的 Namespacehandler, 并找到namespace與之對應的進行解析
BeanFactoryPostProcessor: 對這個名詞進行分割; BeanFactoryPostProcessor = BeanFactory(bean工廠) + Post(后) + Processor(處理器); 這樣就好理解了!
對于這個類, 最常見的就是用于修改 Bean 的屬性, 比如一開始在對應屬性上面設置 “${}” 設置這樣的占位符, 然后通過對應類進行賦值
實現BeanFactoryPostProcessor接口, 其中定義執行方法的主邏輯, 并留下模板方法
processorProperties(), 留給子類去實現
properties 文件里面寫入 beanName.fieldname=fieldvalue 這樣的鍵值對來進行解析數據
這兩個類都是繼承PropertyResourceConfigurer, 但與PropertyOverrideConfigurer不同的是,
這兩個類都是通過"${}" 這種占位符號來確定哪個數據需要替換成 Properties 里面的數據, 其中還涉及到了非常有效的工具類
PlaceholderResolver(這個類是來獲取對應數據中所有的 ${} 中的數據, 其中涉及到遞歸獲取,
這種獲取的方式其實很值得借鑒), 當然還有我們熟悉的設計模式 --> 觀察者模式, 通過 BeanDefinition 的觀察者
BeanDefinitionVisitor 來進行 visitor 每一個
BeanDefinition中的屬性(見BeanDefinitionVisitor)
BeanPostProcessor 接口是Spring IOC 中擴展機制的提現, 自己可以在BeanPostProcessor的回調方法中實現 "實例化邏輯, 依賴分析"等;
它具有以下特定:
—> postProcessPropertyValues 主要是將 bean中被 @Autowired,@Resource, @PersistenceContext, @Required 注解修飾屬性注入對應的值);而在Spring容器自己提供的幾個 BeanPostProcesser的實現類中, 可以看到在 ApplicationContextAwareProcessor 中完成了一下Aware 接口的喚醒
(PS: 針對這兩個類, 在介紹AOP中會詳細解析)
AbstractAutowireCapableBeanFactory.determineConstructorsFromBeanPostProcessors方法
AbstractAutoProxyCreator中這個方法與postProcessAfterInitialization中只有一個會執行
Bean 的代理過程(PS: 這是為什么呢, 看看 earlyProxyReferences就知道了, 兩個方法在執行wrapIfNecessary 都會通過 earlyProxyReferences 判斷是否已經創建好 AOP 對象); 對于這個getEarlyBeanReference 方法是在 Bean 被提早暴露時才會調用
舉例:
兩個類: class A { B b;}; class B { A a; }; 都注入到 Spring 容器,
singletonsCurrentlyInCreation(表示類是否在創建) 中, 則將 A 包裝成 ObjectFactory 類,
放入單例工廠中, 已備被其他創建的類引用, 而 ObjectFactory 中是通過
AbstractAutowireCapableBeanFactory.getEarlyBeanReference -->
SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference -->
AbstractAutoProxyCreator.getEarlyBeanReference 來暴露創建的動態代理對象
AbstractAutowireCapableBeanFactory.getEarlyBeanReference -->
SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference -->
AbstractAutoProxyCreator.getEarlyBeanReference 來得到最終暴露出來的 A 對象
BeanPostProcessor 的分類, 各個子類的作用, 包括 BeanFactory 中如何解決循環引用(只有scope是單例,
并且依賴不是存在在構造函數中才能解決循環引用) 進行了介紹, 后續會對個別 BeanPostProcessor 詳細的解析
IOC 主要組件 FactoryBean
FactoryBean: Spring容器中某類特定Bean的工廠類(這里其實就涉及到了抽象工廠模式), FactoryBean 充斥著整個IOC容器, 一開始接觸 FactoryBean的同學可能不深刻理解這個類的好處, 其實看一下 ProxyFactoryBean(生成動態代理的 FactoryBean), ScheduledExecutorFactoryBean(生成定時線程執行器的 FactoryBean), TransactionProxyFactoryBean(基于目標 Target 生成通過切面控制事務的動態代理類), JobDetailFactoryBean(生成基于 org.quartz.JobDetail 的實例), 就能知道 FactoryBean 主要其實是對于一些創建過程復雜的類進行通過操作的類,
IOC 主要組件 BeanFactory
BeanFactory: 是Spring IOC 容器的頂層接口, 其中定義了獲取Bean的一般接口, 如下:
上面接口主要定義了獲取 Bean 的接口, 至于 BeanFactory 的繼承(HierachicalBeanFactory), 自動裝配(AutowireCapableBeanFactory), 批量獲取 bean(ListableBeanFactory) 這些特性都是由子接口進行定義, 如下圖:
乍一看, 圖中類/接口好復雜, 沒事, 我們一層一層梳理:
接口:第一層: BeanFactory: 這個接口里面定義獲取 Bean 的基礎方法第二層: HierarchicalBeanFactory: 這個接口定義了獲取 parentBeanFactory 的方法(PS: 這里其實也存在著委派機制, 當子BeanFactory 獲取不到bean時, 會通過父BeanFactory來進行獲取, 但是反過來, 父BeanFactory若獲取不到Bean, 則不會再到子BeanFactory中獲取, 這種父子BeanFactory形式通常在于: 在 Tomcat的web.xml里面配置 ContextLoaderListener.contextInitialized --> ContextLoader.initWebApplicationContext --> 通過反射直接創建 XmlWebApplicationContext <-- 這個是 parentBeanFactory, 與之對應的 子BeanFactory 是 由 HttpServletBean.init() --> FrameworkServlet.initServletBean --> FrameworkServlet.initWebApplicationContext --> FrameworkServlet.createWebApplicationContext(這時會將上面的父beanFactory設置到子BeanFactory中)) ListableBeanFactory: (基于類型)獲取容器中所有 BeanNames, 獲取被注解注釋的 BeanName 等方法AutowireCapableBeanFactory: 這里面定義了, 創建Bean, 自動裝配 Bean, 初始化 Bean, 為特定 Bean 解決對應依賴屬性的方法第三層: ConfigurableBeanFactory: 增加配置Bean功能的BeanFactory, 這個接口, 繼承 SingletonBeanFactory, 具備了注冊單例bean的接口, 除此之外 其中還定義了配置 ConversionServere(Spring里面的類型轉換器, Spring 默認的是 DefaultConversionService, 主要是完成數據類型的轉換器的注冊); 配置 BeanPostProcessor(Bean后置處理器)的方法, 銷毀 Bean的方法ConfigurableListableBeanFactory: 這個接口里面定義了 preInstantiateSingletons (預實例化Bean的方法), 獲取指定名稱的 BeanDefinition 的方法等(PS: 這個第三層接口主要是針對第二層接口的功能延伸, 其中 ConfigurableBeanFactory 具備了 注冊單例Bean的功能) 實現類:AbstractBeanFactory: 這個類繼承了 FactoryBeanRegistrySupport 具備了注冊單例+支持FactoryBean的能力, 本身也實現了創建 Bean 的主邏輯(getBean 方法), 并留下了 createBean, getBeanDefinition, containsBeanDefinition 等模板方法AbstractAutowireCapableBeanFactory: 這個類繼承了 AbstractBeanFactory 并實現了模板方法 createBean, 并實現了接口 AutowireCapableBeanFactory, 完成了對 BeanPostProcessor 的處理DefaultListableBeanFactory: 這個類是 Spring IOC 里面功能最全的 Bean 容器, 我們常用的 ApplicationContext 都是基本都是在內部有個 DefaultListableBeanFactory(基于 ApplicationContext 繼承 BeanFactory, 所以你也可以將這種程序設計方式視為 代理方式) PS: 一開始看 Spring IOC 時被 BeanFactory 的繼承接口給嚇住了(哇 這么多的接口繼承關系, 層級又是這么多), 結果才發現, 這么多接口中只有第一層與第二層接口之間有明顯的擴展, 而下面的接口, 都是逐漸逐漸擴充對應的方法上面是 BeanFactory 的接口設計及實現, 接口層面主要是3級, 都是對容器功能一點一點的擴充, 而其中 AbstractBeanFactory 設計了創建類的主邏輯(getBean方法), 并且預留了創建類的模板方法(createBean), 而最終的實現類就是 DefaultListableBeanFactory, 工作中常用的:
XmlBeanFactory(基于 XmlBeanDefinitionReader讀取xml配置的Bean依賴關系)
XmlWebApplicationContext(用于web容器中的BeanFactory)
ClassPathXmlApplicationContext(通過在 classpath 下面查找指定文件的 BeanFactory)
FileSystemXmlApplicationContext(直接通過文件相對路基獲取xml配置文件的 BeanFactory)
AnnotationConfigWebApplicationContext (通過掃描指定目錄下面的 class 文件, 并將被特定注解修飾的額類加載到容器中的 BeanFactory)
都是或多或少的用到 DefaultListableBeanFactory(要是繼承, 要么就是將DefaultListableBeanFactory設置為其內部的一個變量)
IOC 總結
上面簡單的介紹了一下 Spring IOC 里面的主要組件, 其中涉及到的組件還是比較多的, 一開始進行看源碼時可能被浩瀚的代碼給淹沒, 從而恐懼, 接著就放棄了(相信很多人都會經歷過這幾個過程), 當然我也經歷過, 當然直到現在自己對 Spring 里面有些細節的東西還不是非常了解, 但其實沒關系, 個人覺得只要把握主要的設計思想以及常見的架構設計就可以了!(畢竟全部弄精還是非常耗時的一個工程)
https://www.jianshu.com/p/2e6103f571f1
總結
以上是生活随笔為你收集整理的Spring IOC 组件概述的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2020年中国新世代用户视频消费行为洞察
- 下一篇: gradle idea java ssm