javascript
从源码深处体验Spring核心技术--IOC容器初体验
開局經驗之談:可能從這一篇文章開始,小伙伴們都會有點暈車的感覺了,但是這個系列并不是只是介紹下spring表面的一些膚淺的東西,本系列的目的是為了讓大家從源碼層次深入理解Spring,這也是大家在未來的求職道路上的一個重要的漲薪手段,希望小伙伴都不要放棄,結合源碼多看幾遍,努力一定會有收獲。
再談 IOC 與 DI
IOC(Inversion of Control)控制反轉:所謂控制反轉,就是把原先我們代碼里面需要實現的對象創建、依賴的代碼,反轉給容器來幫忙實現。那么必然的我們需要創建一個容器,同時需要一種描述來讓容器知道需要創建的對象與對象的關系。
這個描述最具體表現就是我們所看到的配置文件。
DI(Dependency Injection)依賴注入:就是指對象是被動接受依賴類而不是自己主動去找,換句話說就是指對象不是從容器中查找它依賴的類,而是在容器實例化對象的時候主動將它依賴的類注入給它。
先從我們自己設計這樣一個視角來考慮:1、對象和對象的關系怎么表示?
可以用 xml,properties 文件等語義化配置文件表示。
2、描述對象關系的文件存放在哪里?
可能是 classpath,filesystem,或者是 URL 網絡資源,servletContext 等。
回到正題,有了配置文件,還需要對配置文件解析。
3、不同的配置文件對對象的描述不一樣,如標準的,自定義聲明式的,如何統一?
在內部需要有一個統一的關于對象的定義,所有外部的描述都必須轉化成統一的描述定義。
4、如何對不同的配置文件進行解析?
需要對不同的配置文件語法,采用不同的解析器。
Spring 核心容器類圖
1、BeanFactory
Spring Bean 的創建是典型的工廠模式,這一系列的 Bean 工廠,也即 IOC 容器為開發者管理對象間的依賴關系提供了很多便利和基礎服務,在 Spring 中有許多的 IOC 容器的實現供用戶選擇和使用,其相互關系如下:
其中 BeanFactory 作為最頂層的一個接口類,它定義了 IOC 容器的基本功能規范BeanFactory 有三 個重要的子類:ListableBeanFactory、HierarchicalBeanFactory 和 AutowireCapableBeanFactory。
但是從類圖中我們可以發現最終的默認實現類是 DefaultListableBeanFactory,它實現了所有的接口。
那為何要定義這么多層次的接口呢?查閱這些接口的源碼和說明發現,每個接口都有它使用的場合,它主要是為了區分在 Spring 內部在操作過程中對象的傳遞和轉化過程時,對對象的數據訪問所做的限制。
例如 ListableBeanFactory 接口表示這些 Bean 是可列表化的,而 HierarchicalBeanFactory 表示的是這些 Bean 是有繼承關系的,也就是每個 Bean 有可能有父 Bean。
AutowireCapableBeanFactory 接口定義 Bean 的自動裝配規則。這三個接口共同定義了 Bean 的集合、Bean 之間的關系、以及 Bean 行為。
最基本的 IOC 容器接口 BeanFactory,來看一下它的源碼:
public interface BeanFactory { //對 FactoryBean 的轉義定義,因為如果使用 bean 的名字檢索 FactoryBean 得到的對象是工廠生成的對象, //如果需要得到工廠本身,需要轉義 String FACTORY_BEAN_PREFIX = "&"; //根據 bean 的名字,獲取在 IOC 容器中得到 bean 實例 Object getBean(String name) throws BeansException; //根據 bean 的名字和 Class 類型來得到 bean 實例,增加了類型安全驗證機制。 <T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;Object getBean(String name, Object... args) throws BeansException; <T> T getBean(Class<T> requiredType) throws BeansException; <T> T getBean(Class<T> requiredType, Object... args) throws BeansException; //提供對 bean 的檢索,看看是否在 IOC 容器有這個名字的 bean boolean containsBean(String name); //根據 bean 名字得到 bean 實例,并同時判斷這個 bean 是不是單例 boolean isSingleton(String name) throws NoSuchBeanDefinitionException; boolean isPrototype(String name) throws NoSuchBeanDefinitionException; boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException; boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException; //得到 bean 實例的 Class 類型 @Nullable Class<?> getType(String name) throws NoSuchBeanDefinitionException;//得到 bean 的別名,如果根據別名檢索,那么其原名也會被檢索出來 String[] getAliases(String name); }在 BeanFactory 里只對 IOC 容器的基本行為作了定義,根本不關心你的 Bean 是如何定義怎樣加載的。
正如我們只關心工廠里得到什么的產品對象,至于工廠是怎么生產這些對象的,這個基本的接口不關心。
而要知道工廠是如何產生對象的,我們需要看具體的 IOC 容器實現,Spring 提供了許多 IOC 容器的 實 現 。
比 如 GenericApplicationContext , ClasspathXmlApplicationContext 等 。
ApplicationContext 是 Spring 提供的一個高級的 IOC 容器,它除了能夠提供 IOC 容器的基本功能外,還為用戶提供了以下的附加服務。
從 ApplicationContext 接口的實現,我們看出其特點:
1、支持信息源,可以實現國際化。(實現 MessageSource 接口)
2、訪問資源。(實現 ResourcePatternResolver 接口,后面章節會講到)
3、支持應用事件。(實現 ApplicationEventPublisher 接口)
2、BeanDefinition
SpringIOC 容器管理了我們定義的各種 Bean 對象及其相互的關系,Bean 對象在 Spring 實現中是以 BeanDefinition 來描述的,其繼承體系如下:
3、BeanDefinitionReader
Bean 的解析過程非常復雜,功能被分的很細,因為這里需要被擴展的地方很多,必須保證有足夠的靈活性,以應對可能的變化。
Bean 的解析主要就是對 Spring 配置文件的解析。這個解析過程主要通過BeanDefintionReader 來完成,最后看看 Spring 中 BeanDefintionReader 的類結構圖:
通過本章內容的分析,我們對 Spring 框架體系有了一個基本的宏觀了解,希望小伙伴們好好理解,最好在腦海中形成畫面,為以后的學習打下良好的鋪墊。Web IOC 容器初體驗
我們還是從大家最熟悉的 DispatcherServlet 開始,我們最先想到的還是 DispatcherServlet 的 init()方法。我們發現在 DispatherServlet 中并沒有找到 init()方法。
但是經過探索,往上追索在其父類HttpServletBean 中找到了我們想要的 init()方法,如下:
Web IOC 容器初體驗
我們還是從大家最熟悉的 DispatcherServlet 開始,我們最先想到的還是 DispatcherServlet 的 init()方法。我們發現在 DispatherServlet 中并沒有找到 init()方法。
但是經過探索,往上追索在其父類HttpServletBean 中找到了我們想要的 init()方法,如下:
@Override public final void init() throws ServletException { if (logger.isDebugEnabled()) { logger.debug("Initializing servlet '" + getServletName() + "'"); }// Set bean properties from init parameters. PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); if (!pvs.isEmpty()) { try { //定位資源 BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); //加載配置信息 ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); initBeanWrapper(bw); bw.setPropertyValues(pvs, true); }catch (BeansException ex) { if (logger.isErrorEnabled()) { logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex); }throw ex; } }// Let subclasses do whatever initialization they like.initServletBean(); if (logger.isDebugEnabled()) { logger.debug("Servlet '" + getServletName() + "' configured successfully"); } }在 init()方法中,真正完成初始化容器動作的邏輯其實在 initServletBean()方法中,我們繼續跟進initServletBean()中的代碼在 FrameworkServlet 類中:
protected final void initServletBean() throws ServletException { getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'"); if (this.logger.isInfoEnabled()) { this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started"); }long startTime = System.currentTimeMillis(); try { this.webApplicationContext = initWebApplicationContext(); initFrameworkServlet(); }catch (ServletException ex) { this.logger.error("Context initialization failed", ex); throw ex; }catch (RuntimeException ex) { this.logger.error("Context initialization failed", ex); throw ex; }if (this.logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " + elapsedTime + " ms"); } }在上面的代碼中終于看到了我們似曾相識的代碼 initWebAppplicationContext(),繼續跟進:
從上面的代碼中可以看出,在 configAndRefreshWebApplicationContext()方法中,調用 refresh()方法,這個是真正啟動 IOC 容器的入口,后面會詳細介紹。
IOC 容器初始化以后,最后調用了DispatcherServlet 的 onRefresh()方法,在 onRefresh()方法中又是直接調用 initStrategies()方法初始化 SpringMVC 的九大組件:
?
總結
以上是生活随笔為你收集整理的从源码深处体验Spring核心技术--IOC容器初体验的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 构建 Spring5 源码工程,开启研读
- 下一篇: 从源码深处体验Spring核心技术--基