目錄
最近一直在研究Spring IOC、DI、AOP、MVC幾個步驟的源碼,畫出幾種的時序圖,方便以后查看回憶源碼
1、IOC容器初始化
IOC 容器的初始化包含了BeanDefinition資源文件Resource定位、解析加載和注冊三個步驟。
步驟依次為 : 讀取資源文件為InputStream --> 轉為Resource -> Document -> 解析元素節點封裝為 BeanDefinition
-> 放入IOC容器中,即DefaultListableBeanFactory的 Map<String, BeanDefinition> beanDefinitionMap
主要的幾個類
BeanFactory : BeanFactory及其子類也就是我們說的IOC容器,Spring提供了多種IOC容器以供選擇
ListableBeanFactory接口表示Bean可列表化、HierarchicalBeanFactory接口表示Bean有繼承關系、AutowireCapableBeanFactory定義自動裝配;
以上三個接口的默認實現均為 org.springframework.beans.factory.support.DefaultListableBeanFactory
BeanDefinition : 描述Bean在資源文件中的配置信息及相互關系
BeanDefinitionReader : 解析資源為BeanDefinition
1.1 基於xml的Ioc容器初始化
入口:
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class IoCTest {
public static void main(String[] args) {
//
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
applicationContext.getBean("myUser");
}
}
IOC實現流程及原理
1.2 基於web的Ioc容器初始化
入口: web Ioc容器初始化由DispatcherServlet.init()方法開始
DispatcherServlet.init() 其實是調用的父類 org.springframework.web.servlet.HttpServletBean#init方法,之后
-> org.springframework.web.servlet.FrameworkServlet#initServletBean
-> org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext
-> org.springframework.web.servlet.FrameworkServlet#configureAndRefreshWebApplicationContext
-> org.springframework.context.support.AbstractApplicationContext#refresh
-> org.springframework.context.support.AbstractApplicationContext#obtainFreshBeanFactory
到此,余下 web Ioc容器的初始化步驟和 XML IOC容器的初始化都經由 AbstractApplicationContext#obtainFreshBeanFactory 來完成,和上面時序圖一樣。
1.3 基於注解的Ioc容器初始化
component-scan 掃描指定包路徑,解析class頭的注解,及注解名稱,封裝為BeanDefinition;之后反射獲取屬性及value,封裝為PropertyValue,賦值給BeanDefinition,最后放入Spring IOC容器中。
2、DI依賴注入時序圖
入口: org.springframework.context.support.ClassPathXmlApplicationContext#getBean(java.lang.String)
-> org.springframework.context.support.AbstractApplicationContext#getBean(java.lang.String)
實現原理: 反射為屬性賦值,如果存在循環依賴,使用三級緩存來處理:
一級緩存: 類成功實例化,屬性也成功賦值
二級緩存: 類成功實例化,屬性存在循環依賴、不能賦值
三級緩存: 類不能成功實例化 (指定使用構造器方法進行實例化,構造器中的參數存在循環引用)
在初始化完畢,會先遍歷三級緩存進行依賴注入,放入一級緩存;然后遍歷二級緩存,放入一級緩存;
3、AOP時序圖
入口: org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition)
實現邏輯:
基於JDK或者Cglib的動態代理通過字節碼重組實現;
首先加載配置的切面PointCut及方法攔截通知Advice的配置,在進行依賴注入時,為每個類及類的每個方法正則匹配是否符合切面表達式,符合的話判斷該類是否實現了接口,實現接口的話使用JDK的動態代理,否則使用Cglib的動態代理字節碼重組重新織入通知代碼,以次生產新的代理類。
AdvisedSupport -> AopConfig -> Advice -> JDKProxy or CglibProxy
4、MVC流程時序圖
入口: DispatcherServlet.init() && DispatcherServlet.doService()
實現邏輯:
首先程序啟動初始化Spring九大組件:
初始化url映射HandlerMapping; 掃描Controller下的所有RequestMapping,組成正則類型的url Pattern,並與Controller、method綁定關系組成HandlerMapping對象;
初始化參數適配器HandlerAdapter;記錄RequestMapping對應method的參數類型,參數名稱,RequestParam綁定名稱等
初始化視圖解析器ViewResolver;
....
之后再頁面url發出請求后,根據請求url正則匹配到對應1到多個HandlerMapping,然后找出對應的參數適配器HandlerAdapter,反射進行方法調用返回ModelAndView,再由ViewResolver進行解析、占位符替換,最后對View渲染,將html或jsp通過Response返回
DispatcherServlet -> HandlerMapping -> HandlerAdapter -> ModelAndView -> ViewResolver -> View