Spring MVC DispatcherServlet
- DispatcherServlet UML圖
本次分析:HttpServlet->HttpServletBean->FramworkServlet->DispacherServlet
ServletContext是什麽?
ServletContext,是一個全局的儲存信息的空間,服務器開始,其就存在,服務器關閉,其才釋放。request,一個用戶可有多個;session,一個用戶一個;而servletContext,所有用戶共用一個。所以,為了節省空間,提高效率,ServletContext中,要放必須的、重要的、所有用戶需要共享的線程又是安全的一些信息。WebApplicationContext是什麽?
顧名思義WebApplicationContext是依賴於Web容器的一個Spring的IOC容器。前提條件是web容器啟動後這個容器才能啟動。那麽如何借助web容器來啟動Spring web的上下文?HttpServletBean類
public final void init() throws ServletException { if(this.logger.isDebugEnabled()) { this.logger.debug("Initializing servlet ‘" + this.getServletName() + "‘"); } //將Servlet初始化參數設置到該組件上 PropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties); if(!pvs.isEmpty()) { try { BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext()); bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment())); this.initBeanWrapper(bw); bw.setPropertyValues(pvs, true); } catch (BeansException var4) { if(this.logger.isErrorEnabled()) { this.logger.error("Failed to set bean properties on servlet ‘" + this.getServletName() + "‘", var4); } throw var4; } } //交給子類初始化,FrameworkServlet子類的方法覆蓋 this.initServletBean(); if(this.logger.isDebugEnabled()) { this.logger.debug("Servlet ‘" + this.getServletName() + "‘ configured successfully"); } }
FrameworkServlet類
protected final void initServletBean() throws ServletException { //省略 // try { //初始化web上下文 this.webApplicationContext = this.initWebApplicationContext(); //初始化 this.initFrameworkServlet(); } //省略
protected WebApplicationContext initWebApplicationContext() {
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
//構建並註入一個上下文實例
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) {
//查找已經綁定的上下文
wac = findWebApplicationContext();
}
if (wac == null) {
// No context instance is defined for this servlet -> create a local one 指定為ContextLoaderListener
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;
}
從initWebApplicationContext()方法可以看出,基本上如果ContextLoaderListener加載了上下文將作為根上下文(DispatcherServlet的父容器)。
- DispatcherServlet類
```
**- This implementation calls {@link #initStrategies}.
*/
@Override
//FrameworkServlet中的wac
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
- Initialize the strategy objects that this servlet uses.
May be overridden in subclasses in order to initialize further strategy objects.
*/
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
//將兩種類型的Bean從context裏取出來
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
- This implementation calls {@link #initStrategies}.
doDispatch
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
//省略...
try {
//省略...
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
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;
}
}
//調用所有攔截器的prehandle
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
//調用註冊的所有攔截器的posthandle方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
//省略...
//render繪制MV
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
//省略...
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
//省略...
}
}
```
1.容器啟動時,找到配置文件中的context-param作為鍵值對放到ServletContext中
2.然後找到listener,容器調用它的contextInitialized(ServletContextEvent event)方法,執行其中的操作
3.spring為我們提供了實現ServletContextListener接口的上下文初始化監聽器:ContextLoaderListener
4.spring為我們提供的IOC容器,需要我們指定容器的配置文件,然後由該監聽器初始化並創建該容器。要求你指定配置文件的地址及文件名稱,一定要使用:contextConfigLocation作為參數名稱。
- spring上下文容器配置後,初始化了什麽
1、servlet容器啟動,為應用創建一個“全局上下文環境”:ServletContext
2、容器調用web.xml中配置的contextLoaderListener,初始化WebApplicationContext上下文環境(即IOC容器),加載context-param指定的配置文件信息到IOC容器中。WebApplicationContext在ServletContext中以鍵值對的形式保存
3、容器初始化web.xml中配置的servlet,為其初始化自己的上下文信息servletContext,並加載其設置的配置信息到該上下文中。將WebApplicationContext設置為它的父容器。
4、此後的所有servlet的初始化都按照3步中方式創建,初始化自己的上下文環境,將WebApplicationContext設置為自己的父上下文環境。
Spring MVC DispatcherServlet