詳解springMVC容器加載源碼分析
springmvc是一個(gè)基于servlet容器的輕量靈活的mvc框架,在它整個(gè)請求過程中,為了能夠靈活定制各種需求,所以提供了一系列的組件完成整個(gè)請求的映射,響應(yīng)等等處理。這里我們來分析下springMVC的源碼。
首先,spring提供了一個(gè)處理所有請求的servlet,這個(gè)servlet實(shí)現(xiàn)了servlet的接口,就是DispatcherServlet。把它配置在web.xml中,并且處理我們在整個(gè)mvc中需要處理的請求,一般如下配置:
<servlet>
<servlet-name>spring-servlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring-servlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
DispatcherServlet也繼承了FrameworkServlet抽象類,這個(gè)抽象類為整個(gè)servlet的處理提供了spring容器的支持,讓原本servlet容器的DispatcherServlet擁有能夠利用spring容器組件的能力。上面servlet的初始化參數(shù)contextConfigLocation就是DispatcherServlet獲取spring容器的配置環(huán)境。FrameworkServlet繼承自org.springframework.web.servlet.HttpServletBean。HttpServletBean實(shí)現(xiàn)了servlet容器初始化會(huì)調(diào)用的init函數(shù)。這個(gè)init函數(shù)會(huì)調(diào)用FrameworkServlet的初始化加載spring容器方法。方法源碼:
@Override
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 {
// 初始化spring-servlet容器
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");
}
}
可以看到這里就觸發(fā)了spring的對(duì)web支持的容器初始化,這里使用的容器為WebApplicationContext.接下來我們就來分析一下整個(gè)容器的初始化過程:
protected WebApplicationContext initWebApplicationContext() {
// 查看是否在servlet上下文有所有容器需要繼承的根Web容器
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
// 如果容器已經(jīng)加載
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent -> set
// the root application context (if any; may be null) as the parent
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
// No context instance was injected at construction time -> see if one
// has been registered in the servlet context. If one exists, it is assumed
// that the parent context (if any) has already been set and that the
// user has performed any initialization such as setting the context id
wac = findWebApplicationContext();
}
if (wac == null) {
// 創(chuàng)建容器
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
// Either the context is not a ConfigurableApplicationContext with refresh
// support or the context injected at construction time had already been
// refreshed -> trigger initial onRefresh manually here.
onRefresh(wac);
}
if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
"' as ServletContext attribute with name [" + attrName + "]");
}
}
return wac;
}
如果已經(jīng)有容器被創(chuàng)建那就初始化。否則創(chuàng)建容器,創(chuàng)建邏輯:
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
Class<?> contextClass = getContextClass();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Servlet with name '" + getServletName() +
"' will try to create custom WebApplicationContext context of class '" +
contextClass.getName() + "'" + ", using parent context [" + parent + "]");
}
// 判斷是否是可配置的容器
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException(
"Fatal initialization error in servlet with name '" + getServletName() +
"': custom WebApplicationContext class [" + contextClass.getName() +
"] is not of type ConfigurableWebApplicationContext");
}
// 如果找到目標(biāo)容器,并且可配置,然后就開始獲取配置文件并開始初始化
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
wac.setEnvironment(getEnvironment());
wac.setParent(parent);
wac.setConfigLocation(getContextConfigLocation());
// 這里是獲取配置文件和完成初始化web容器的入口
configureAndRefreshWebApplicationContext(wac);
return wac;
}
這里完成了 web容器的相關(guān)準(zhǔn)備工作,開始正式讀取配置文件加載和初始化容器。
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
// 給容器設(shè)置一個(gè)id
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// The application context id is still set to its original default value
// -> assign a more useful id based on available information
if (this.contextId != null) {
wac.setId(this.contextId);
}
else {
// Generate default id...
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
}
}
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
// The wac environment's #initPropertySources will be called in any case when the context
// is refreshed; do it eagerly here to ensure servlet property sources are in place for
// use in any post-processing or initialization that occurs below prior to #refresh
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
}
/* 這里在容器被激活后,
并且容器還沒完成初始化之前可以對(duì)容器的相關(guān)配置做一些修改,
默認(rèn)給了空實(shí)現(xiàn),
這里子類可以去重寫,能夠獲得在容器初始化之前做 一些處理*/
postProcessWebApplicationContext(wac);
// 這里講容器的初始化信息放到一個(gè)列表
applyInitializers(wac);
// 這里就開始web容器的初始化
wac.refresh();
}
容器的初始化在AbstractApplicationContext,無論是其他的容器,最終都會(huì)調(diào)用到refresh()函數(shù),這個(gè)函數(shù)基本定義了整個(gè)容器初始化的整個(gè)脈絡(luò),這里不展開講,本博客之后應(yīng)該會(huì)詳細(xì)分析這塊的邏輯,這里大概的注釋一下每一個(gè)函數(shù)完成的操作:
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 這里主要加載了容器當(dāng)中一些從其他配置文件讀取的變量
prepareRefresh();
// 獲取容器本身
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 這里完成一些基礎(chǔ)組件的依賴
prepareBeanFactory(beanFactory);
try {
// 添加 容器初始化之前的前置處理
postProcessBeanFactory(beanFactory);
// 調(diào)用 前置處理器,其中包含invokeBeanDefinitionRegistryPostProcessors與invokeBeanFactoryPostProcessors兩類前置處理器的調(diào)用
invokeBeanFactoryPostProcessors(beanFactory);
// 注冊bean被創(chuàng)建之前的前置處理器
registerBeanPostProcessors(beanFactory);
// 初始化容器的編碼源
initMessageSource();
// 初始化一些事件監(jiān)聽器
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// 注冊容器監(jiān)聽器
registerListeners();
// 初始化所有非懶加載的beans
finishBeanFactoryInitialization(beanFactory);
// Last step: 事件通知關(guān)心容器加載的相關(guān)組件
finishRefresh();
}
// 部分代碼省略
}
}
}
自此加載完畢核心容器,然后回到FramewordServlet的initWebApplicationContext函數(shù),在調(diào)用createWebApplicationContext完成一系列上面的操作之后,需要mvc servlet組件,入口就是onRefresh(ApplocationContext context)方法。它會(huì)調(diào)用另一個(gè)方法initStrategies(ApplicationContext context)。該方法如下:
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
// 獲取所有的RequestMappings
initHandlerMappings(context);
// 不同handler的適配器
initHandlerAdapters(context);
// 異常解析器
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
這里我們重點(diǎn)講解initHandlerMappings與initHandlerAdapters函數(shù),因?yàn)檫@兩個(gè)是處理servlet請求的入口。
在spring mvc中任何類都可以處理request請求,因?yàn)镈ispacherServlet也是實(shí)現(xiàn)了HttpServlet的接口,所以處理請求也是doService里。doService會(huì)將請求交給doDispatcher函數(shù)處理。然后doDispacher的源碼:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 這里獲取當(dāng)前請求的處理handler
mappedHandler = getHandler(processedRequest, false);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
/* 因?yàn)閔andler可以是任何類,
但是我們的DispacherServlet需要一個(gè)統(tǒng)一的處理接口,這個(gè)接口就是HandlerAdapter,
不同的HandlerMapping可以獲取到各種各樣的Handler,
這個(gè)handler最后都必須得到HandlerAdapter的支持才能被DispacherServlet所調(diào)用。
擴(kuò)充一個(gè)新的HandlerMapping只需要添加一個(gè)新的HandlerAdatper即可,有點(diǎn)抽象工廠模式的味道*/
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(request, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Error err) {
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
但是我們發(fā)現(xiàn)獲取到的handler并不是Object而是一個(gè)HandlerExecutionChain,這個(gè)類可以進(jìn)去查看發(fā)現(xiàn)是一堆攔截器和一個(gè)handler,主要就是給每一個(gè)請求一個(gè)前置處理的機(jī)會(huì),這里值得一提的是一般來說攔截器和過濾器的區(qū)別就是攔截器可以終止后續(xù)執(zhí)行流程,而過濾器一般不終止。過濾器一般是容器級(jí)別的,這個(gè)handler前置攔截器可以做到更細(xì)級(jí)別的控制,例如過濾器只定義了init和doFilter,但是這個(gè)handler攔截器定義了preHandle和postHandle還有afterCompletion函數(shù),不難理解分別對(duì)應(yīng)不同請求階段的攔截粒度,更加靈活。
獲取處理handler的getHandler代碼:
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 這里獲取多個(gè)映射方式,一旦找到合適的處理請求的handler即返回
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
// HandlerExecutionChain包含了
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
HandlerAdapter處理后返回統(tǒng)一被封裝成ModelAndView對(duì)象,這個(gè)就是包含試圖和數(shù)據(jù)的對(duì)象,在經(jīng)過適當(dāng)?shù)奶幚恚?/p>
processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception)
將頁面與返回的數(shù)據(jù)返回給瀏覽器完成整個(gè)請求過程。以上就是springMVC大概的啟動(dòng)過程和請求的處理過程,之后本博客還會(huì)陸續(xù)分析核心的spring源碼,博主一直認(rèn)為精讀著名框架源碼是在短時(shí)間提升代碼能力的捷徑,因?yàn)檫@些輪子包含太多設(shè)計(jì)思想和細(xì)小的代碼規(guī)范,這些讀多了都會(huì)潛移默化的形成一種本能的東西。設(shè)計(jì)模式也不斷貫穿其中。
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Maven高級(jí)的聚合和繼承的實(shí)現(xiàn)
在軟件開發(fā)中,隨著項(xiàng)目規(guī)模的擴(kuò)大,單個(gè)模塊的開發(fā)方式逐漸轉(zhuǎn)變?yōu)槎嗄K開發(fā),這種方式帶來了項(xiàng)目管理上的挑戰(zhàn),其中最常見的問題是模塊間的依賴管理和版本控制問題,本文就來介紹一下2024-10-10
MyBatis-Plus 如何單元測試的實(shí)現(xiàn)
這篇文章主要介紹了MyBatis-Plus 如何單元測試的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08
詳解SpringBoot中Controller接收對(duì)象列表實(shí)現(xiàn)
這篇文章主要介紹了詳解SpringBoot中Controller接收對(duì)象列表實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05
基于IDEA 的遠(yuǎn)程調(diào)試 Weblogic的操作過程
這篇文章主要介紹了基于IDEA 的遠(yuǎn)程調(diào)試 Weblogic的操作過程,本文通過圖文實(shí)例相結(jié)合給大家介紹的非常詳細(xì),需要的朋友可以參考下2021-09-09
MyBatis insert操作插入數(shù)據(jù)之后返回插入記錄的id
今天小編就為大家分享一篇關(guān)于MyBatis插入數(shù)據(jù)之后返回插入記錄的id,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-03-03
Java面試題-實(shí)現(xiàn)復(fù)雜鏈表的復(fù)制代碼分享
這篇文章主要介紹了Java面試題-實(shí)現(xiàn)復(fù)雜鏈表的復(fù)制代碼分享,小編覺得還是挺不錯(cuò)的,具有參考價(jià)值,需要的朋友可以了解下。2017-10-10

