javascript
Spring中DispacherServlet与WebApplicationContext、ServletContext的关系和工作机制
解釋一:
? ? 要想很好理解這三個上下文的關系,需要先熟悉spring是怎樣在web容器中啟動起來的。spring的啟動過程其實就是其IoC容器的啟動過程,對于web程序,IoC容器啟動過程即是建立上下文的過程。
spring的啟動過程:
首先,對于一個web應用,其部署在web容器中,web容器提供其一個全局的上下文環境,這個上下文就是ServletContext,其為后面的spring IoC容器提供宿主環境;
其次,在web.xml中會提供有contextLoaderListener。在web容器啟動時,會觸發容器初始化事件,此時contextLoaderListener會監聽到這個事件,其contextInitialized方法會被調用,在這個方法中,spring會初始化一個啟動上下文,這個上下文被稱為根上下文,即WebApplicationContext,這是一個接口類,確切的說,其實際的實現類是XmlWebApplicationContext。這個就是spring的IoC容器,其對應的Bean定義的配置由web.xml中的context-param標簽指定。在這個IoC容器初始化完畢后,spring以WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE為屬性Key,將其存儲到ServletContext中,便于獲取;
再次,contextLoaderListener監聽器初始化完畢后,開始初始化web.xml中配置的Servlet,這個servlet可以配置多個,以最常見的DispatcherServlet為例,這個servlet實際上是一個標準的前端控制器,用以轉發、匹配、處理每個servlet請求。DispatcherServlet上下文在初始化的時候會建立自己的IoC上下文,用以持有spring mvc相關的bean。在建立DispatcherServlet自己的IoC上下文時,會利用WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE先從ServletContext中獲取之前的根上下文(即WebApplicationContext)作為自己上下文的parent上下文。有了這個parent上下文之后,再初始化自己持有的上下文。這個DispatcherServlet初始化自己上下文的工作在其initStrategies方法中可以看到,大概的工作就是初始化處理器映射、視圖解析等。這個servlet自己持有的上下文默認實現類也是mlWebApplicationContext。初始化完畢后,spring以與servlet的名字相關(此處不是簡單的以servlet名為Key,而是通過一些轉換,具體可自行查看源碼)的屬性為屬性Key,也將其存到ServletContext中,以便后續使用。這樣每個servlet就持有自己的上下文,即擁有自己獨立的bean空間,同時各個servlet共享相同的bean,即根上下文(第2步中初始化的上下文)定義的那些bean。
解釋二:
? ? 在Web容器(比如Tomcat)中配置Spring時,你可能已經司空見慣于web.xml文件中的以下配置代碼: [plain]?view plain?copy? ? 以上配置首先會在ContextLoaderListener中通過<context-param>中的applicationContext.xml創建一個ApplicationContext,再將這個ApplicationContext塞到ServletContext里面,通過ServletContext的setAttribute方法達到此目的,在ContextLoaderListener的源代碼中,我們可以看到這樣的代碼:
[java]?view plain?copy? ? 以上由ContextLoaderListener創建的ApplicationContext是共享于整個Web應用程序的,而你可能早已經知道,DispatcherServlet會維持一個自己的ApplicationContext,默認會讀取/WEB-INFO/<dispatcherServletName>-servlet.xml文件,而我么也可以重新配置:
[plain]?view plain?copy? ? 問題是:以上兩個ApplicationContext的關系是什么,它們的作用作用范圍分別是什么,它們的用途分別是什么?
? ? ContextLoaderListener中創建ApplicationContext主要用于整個Web應用程序需要共享的一些組件,比如DAO,數據庫的ConnectionFactory等。而由DispatcherServlet創建的ApplicationContext主要用于和該Servlet相關的一些組件,比如Controller、ViewResovler等。
? ? 對于作用范圍而言,在DispatcherServlet中可以引用由ContextLoaderListener所創建的ApplicationContext,而反過來不行。
? ? 在Spring的具體實現上,這兩個ApplicationContext都是通過ServletContext的setAttribute方法放到ServletContext中的。但是,ContextLoaderListener會先于DispatcherServlet創建ApplicationContext,DispatcherServlet在創建ApplicationContext時會先找到由ContextLoaderListener所創建的ApplicationContext,再將后者的ApplicationContext作為參數傳給DispatcherServlet的ApplicationContext的setParent()方法,在Spring源代碼中,你可以在FrameServlet.java中找到如下代碼:
wac.setParent(parent);? ? 其中,wac即為由DisptcherServlet創建的ApplicationContext,而parent則為有ContextLoaderListener創建的ApplicationContext。此后,框架又會調用ServletContext的setAttribute()方法將wac加入到ServletContext中。
? ? 當Spring在執行ApplicationContext的getBean時,如果在自己context中找不到對應的bean,則會在父ApplicationContext中去找。這也解釋了為什么我們可以在DispatcherServlet中獲取到由ContextLoaderListener對應的ApplicationContext中的bean。Spring API中的解釋:
public interface WebApplicationContextextends?ApplicationContext
Interface to provide configuration for a web application. This is read-only while the application is running, but may be reloaded if the implementation supports this.
This interface adds a?getServletContext()?method to the generic ApplicationContext interface, and defines a well-known application attribute name that the root context must be bound to in the bootstrap process.
Like generic application contexts, web application contexts are hierarchical. There is a single root context per application, while each servlet in the application (including a dispatcher servlet in the MVC framework) has its own child context.
In addition to standard application context lifecycle capabilities, WebApplicationContext implementations need to detect?ServletContextAware?beans and invoke the?setServletContext?method accordingly.
翻譯:
公共接口WebApplicationContext
擴展ApplicationContext
提供Web應用程序配置的接口。 這在應用程序運行時是只讀的,但是如果實現支持這個,可以重新加載。
該接口將一個getServletContext()方法添加到通用ApplicationContext接口,并定義一個眾所周知的應用程序屬性名稱,該名稱在引導進程中必須綁定根上下文。
像通用應用程序上下文一樣,Web應用程序上下文是分層的。 每個應用程序都有一個根上下文,而應用程序中的每個servlet(包括MVC框架中的調度器servlet)都有自己的子上下文。
除了標準的應用程序上下文生命周期功能,WebApplicationContext實現還需要檢測ServletContextAware bean,并相應地調用setServletContext方法。
工作機制:
1.DispacherServlet是前端控制器(Struts是Filter),負責接收前端請求,并根據請求找到具體的Handler(目前的Handler是方法級別的);SpringMVC中DispacherServlet初始化放在web.xml中,<load-on-start>1</load-on-start>,意思是Servlet容器啟動時自動加載該Servlet。
2.HandlerMapping:負責將URL和controller匹配到一起,簡單來說就是根據URL找到具體的類(根據注解@Controller和@RequestMapping找到);
3.HandlerAdapter:根據URL找到具體類的具體方法;
jdk 具體執行過程如下:
1.DispatcherServlet本質上還是一個Servlet,故需要在web容器里初始化
/*** Initialize the strategy objects that this servlet uses.* <p>May be overridden in subclasses in order to initialize further strategy objects.*/protected void initStrategies(ApplicationContext context) {initMultipartResolver(context);initLocaleResolver(context);initThemeResolver(context); initHandlerMappings(context); // 從ApplicationContext容器中初始生成HandlerMappingsinitHandlerAdapters(context); // 初始化HandlerAdapterinitHandlerExceptionResolvers(context);initRequestToViewNameTranslator(context);initViewResolvers(context);initFlashMapManager(context);}2.當有一個請求到達時,調用DispatcherServlet的doDispatch()方法,該方法找到真正的handler處理該請求;主要代碼如下:
/*** Process the actual dispatching to the handler.* <p>The handler will be obtained by applying the servlet's HandlerMappings in order.* The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters* to find the first that supports the handler class.* <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers* themselves to decide which methods are acceptable.* @param request current HTTP request* @param response current HTTP response* @throws Exception in case of any kind of processing failure*通過查找HandlerMapping找到真正的Handler;先找到handler class,根據該class找到HandlerAdapter; 通過查詢servlet已注冊的HandlerAdapters,找到真正處理該請求的method*/protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;ModelAndView mv = null;// Determine handler for the current request.返回HandlerExecutionChain;mappedHandler = getHandler(processedRequest, false);// Determine handler adapter for the current request.根據HandlerExecutionChain找到HandlerAdapterHandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// Actually invoke the handler.使用給定的handler處理請求;mv = ha.handle(processedRequest, response, mappedHandler.getHandler());}
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {for (HandlerMapping hm : this.handlerMappings) {if (logger.isTraceEnabled()) {logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");}//查詢所有已注冊的HandlerMapping beans找到處理該請求的Handler;HandlerExecutionChain handler = hm.getHandler(request);if (handler != null) {return handler;}}return null;}
其中HandlerExecutionChain是有一個HandlerAdapter和若干個HandlerInteceptor組成,請求先經HandlerInteceptor處理后在傳給Adapter。一般我們使用HandlerInteceptor做一些
登錄驗證,安全驗證等
3.HandlerInterceptor和Servlet里的Filter作用很像,下面講一下關于Filter的東西
3.1? Filter作用?
用于HttpServletRequest預處理和HttpServletResponse后處理;執行流程是 request---->listener----->filter------>struts攔截器(?)----->Servlet,這是鏈式處理過程,請求最后總會到達Servlet的;
3.2 一般用來干啥?
用戶登錄驗證,用戶發來請求,攔截請求后,驗證用戶是否登錄過,如果沒有登錄調到登錄頁面,日志功能,編碼格式。
3.3Filter有三個過程
Filter一般在服務器端工作,故web容器調用它的init(),在web容器初始化的時候創建Filter的實例對象;
chain.doFilter(),把要做的事情都放在doFilter()方法里面完成;
3.4 filter使用配置文件注冊,在web應用程序一啟動,web服務器就會實例化filter對象;
filter實例化
web.xml配置:
<!-- 編碼過濾器 --> <filter> <filter-name>setCharacterEncoding</filter-name> <filter-class>com.company.strutstudy.web.servletstudy.filter.EncodingFilter</filter-class> //該過濾器(EncodingFilter)具體位置。<init-param> <param-name>encoding</param-name> // 設定encoding編碼為utf-8.<param-value>utf-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>setCharacterEncoding</filter-name> <url-pattern>/*</url-pattern> //處理所有頁面的編碼格式</filter-mapping> <!-- 請求url日志記錄過濾器 --> <filter> <filter-name>logfilter</filter-name> <filter-class>com.company.strutstudy.web.servletstudy.filter.LogFilter</filter-class> </filter> <filter-mapping> <filter-name>logfilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> 編碼攔截器:
public class EncodingFilter implements Filter { private String encoding; private Map<String, String> params = new HashMap<String, String>(); // 項目結束時就已經進行銷毀 public void destroy() { System.out.println("end do the encoding filter!"); params=null; encoding=null; } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { //UtilTimerStack.push("EncodingFilter_doFilter:"); System.out.println("before encoding " + encoding + " filter!"); req.setCharacterEncoding(encoding); // resp.setCharacterEncoding(encoding); // resp.setContentType("text/html;charset="+encoding); chain.doFilter(req, resp); System.out.println("after encoding " + encoding + " filter!"); System.err.println("----------------------------------------"); //UtilTimerStack.pop("EncodingFilter_doFilter:"); } // 項目啟動時就已經進行讀取 public void init(FilterConfig config) throws ServletException { System.out.println("begin do the encoding filter!"); encoding = config.getInitParameter("encoding"); for (Enumeration e = config.getInitParameterNames(); e .hasMoreElements();) { String name = (String) e.nextElement(); String value = config.getInitParameter(name); params.put(name, value); } } }
總結
以上是生活随笔為你收集整理的Spring中DispacherServlet与WebApplicationContext、ServletContext的关系和工作机制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何设置iPhone的WIFI?
- 下一篇: 如何在iPhone上用国内邮箱?