閱讀439 返回首頁    go 阿裏雲 go 技術社區[雲棲]


MVC架構探究及其源碼實現(4)-前端控製器

博學,切問,近思--詹子知 (https://jameszhan.github.io)

前端控製器是整個MVC框架中最為核心的一塊,它主要用來攔截符合要求的外部請求,並把請求分發到不同的控製器去處理,根據控製器處理後的結果,生成相應的響應發送到客戶端。前端控製器既可以使用Filter實現(Struts2采用這種方式),也可以使用Servlet來實現。這裏我們就采用後一種方式來實現我們的MVC框架。

 ds

1.配置web.xml,使得我們的前端控製器可以攔截所有符合要求的用戶請求,這裏我們的前端控製器能處理所有以.do結尾的用戶請求。 <?xml version="1.0" encoding="ISO-8859-1"?> <web-app xmlns="https://java.sun.com/xml/ns/javaee" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <description>MVC Sample</description> <display-name>MVC</display-name> <servlet> <servlet-name>DispatcherServlet</servlet-name> <servlet-class>com.google.mvc.web.servlet.DispatcherServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>DispatcherServlet</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> </web-app>   2.FrameworkServlet實現。
FrameworkServlet是DispatcherServlet的直接父類,繼承自HttpServlet,主要用來初始話WebApplicationContext,把不同的Http請求操作委托給同一個方法去處理。package com.google.mvc.web.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.log4j.Logger; import com.google.mvc.web.context.WebApplicationContext; public abstract class FrameworkServlet extends HttpServlet { private static final long serialVersionUID = 1L; private static final Logger LOGGER = Logger.getLogger(FrameworkServlet.class); private WebApplicationContext webApplicationContext; @Override public void init() throws ServletException { if (LOGGER.isDebugEnabled()) { LOGGER.debug("----------Initializing servlet '" + getServletName() + "'----------"); } this.webApplicationContext = initWebApplicationContext(); initServletBean(); if (LOGGER.isDebugEnabled()) { LOGGER.debug("----------Servlet '" + getServletName() + "' configured successfully----------/n/n"); } } private WebApplicationContext initWebApplicationContext() { WebApplicationContext wac = new WebApplicationContext(getServletContext()); wac.init(); onRefresh(wac); return wac; } protected void onRefresh(WebApplicationContext context) { // For subclasses: do nothing by default. } protected void initServletBean(){ } protected abstract void doService(HttpServletRequest request, HttpServletResponse response) throws Exception; protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { long startTime = System.currentTimeMillis(); Throwable failureCause = null; try { doService(request, response); } catch (ServletException ex) { failureCause = ex; throw ex; } catch (IOException ex) { failureCause = ex; throw ex; } catch (Throwable ex) { failureCause = ex; throw new NestedServletException("Request processing failed", ex); } finally { if (failureCause != null) { LOGGER.error("Could not complete request", failureCause); } else { long processingTime = System.currentTimeMillis() - startTime; if (LOGGER.isDebugEnabled()) { LOGGER.info("Successfully completed request, cost " + processingTime + " ms/n"); } } } } @Override protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } @Override protected void doHead(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } @Override protected void doOptions(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } @Override protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } @Override protected void doTrace(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } @Override public void destroy() { if(LOGGER.isDebugEnabled()){ LOGGER.info("Servlet destory"); } super.destroy(); } public WebApplicationContext getWebApplicationContext() { return webApplicationContext; } }   3.DispatcherServlet實現。 package com.google.mvc.web.servlet; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.StringTokenizer; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.log4j.Logger; import com.google.mvc.web.context.WebApplicationContext; public class DispatcherServlet extends FrameworkServlet { private static final long serialVersionUID = 1L; private static final Logger LOGGER = Logger.getLogger(DispatcherServlet.class); private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties"; private static final Properties defaultStrategies = new Properties(); private List<HandlerMapping> handlerMappings; private List<HandlerAdapter> handlerAdapters; private List<ViewResolver> viewResolvers; static { try { defaultStrategies.load(DispatcherServlet.class.getResourceAsStream(DEFAULT_STRATEGIES_PATH)); } catch (IOException ex) { throw new IllegalStateException("Could not load 'DispatcherServlet.properties': " + ex.getMessage()); } } @Override protected void onRefresh(WebApplicationContext wac) { initHandlerMappings(wac); initHandlerAdapters(wac); initViewResolvers(wac); } private void initHandlerMappings(WebApplicationContext wac) { Map<String, HandlerMapping> map = wac.beansOfType(HandlerMapping.class); if (!map.isEmpty()) { this.handlerMappings = new ArrayList<HandlerMapping>(map.values()); } if (this.handlerMappings == null) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default"); } this.handlerMappings = getDefaultStrategies(wac, HandlerMapping.class); } } private void initHandlerAdapters(WebApplicationContext wac) { Map<String, HandlerAdapter> map = wac.beansOfType(HandlerAdapter.class); if (!map.isEmpty()) { this.handlerAdapters = new ArrayList<HandlerAdapter>(map.values()); } if (this.handlerAdapters == null) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("No HandlerAdapters found in servlet '" + getServletName() + "': using default"); } this.handlerAdapters = getDefaultStrategies(wac, HandlerAdapter.class); } } private void initViewResolvers(WebApplicationContext wac) { Map<String, ViewResolver> map = wac.beansOfType(ViewResolver.class); if (!map.isEmpty()) { this.viewResolvers = new ArrayList<ViewResolver>(map.values()); } if (this.viewResolvers == null) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("No ViewResolvers found in servlet '" + getServletName() + "': using default"); } this.viewResolvers = getDefaultStrategies(wac, ViewResolver.class); } } @Override protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { if (LOGGER.isDebugEnabled()) { LOGGER.debug("DispatcherServlet with name '" + getServletName() + "' received request for [" + request.getRequestURI() + "]"); } doDispatch(request, response); } protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Bound request context to thread: " + request); } Object handler = getHandler(request); HandlerAdapter ha = getHandlerAdapter(handler); ModelAndView mv = ha.handle(request, response, handler); // Do we need view name translation? if (mv != null && !mv.hasView()) { mv.setViewName(getDefaultViewName(request)); } // Did the handler return a view to render? if (mv != null && !mv.wasCleared()) { render(mv, request, response); } } protected <T> List<T> getDefaultStrategies(WebApplicationContext wac, Class<T> strategyInterface) { String key = strategyInterface.getName(); List<T> strategies = new ArrayList<T>(); String value = defaultStrategies.getProperty(key); if (value != null) { StringTokenizer token = new StringTokenizer(value, ","); while (token.hasMoreTokens()) { String className = token.nextToken(); try { Class<?> clazz = this.getClass().getClassLoader().loadClass(className); strategies.add((T) wac.createBean(clazz)); } catch (Exception e) { LOGGER.error("Can't load class " + className + "", e); } } } else { strategies = Collections.emptyList(); } return strategies; } protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception { View view = null; if (mv.isReference()) { // We need to resolve the view name. view = resolveViewName(mv.getViewName(), mv.getModelInternal(), request); if (view == null) { throw new ServletException("Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" + getServletName() + "'"); } } else { // No need to lookup: the ModelAndView object contains the actual // View object. view = mv.getView(); if (view == null) { throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " + "View object in servlet with name '" + getServletName() + "'"); } } // Delegate to the View object for rendering. if (LOGGER.isDebugEnabled()) { LOGGER.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'"); } view.render(mv.getModelInternal(), request, response); } protected View resolveViewName(String viewName, Map<String, Object> model, HttpServletRequest request) throws Exception { for (Iterator<ViewResolver> it = this.viewResolvers.iterator(); it.hasNext();) { ViewResolver viewResolver = it.next(); View view = viewResolver.resolveViewName(viewName); if (view != null) { return view; } } return null; } protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { Iterator<HandlerAdapter> it = this.handlerAdapters.iterator(); while (it.hasNext()) { HandlerAdapter ha = it.next(); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Testing handler adapter [" + ha + "]"); } if (ha.supports(handler)) { return ha; } } throw new ServletException("No adapter for handler [" + handler + "]: Does your handler implement a supported interface like Controller?"); } protected Object getHandler(HttpServletRequest request) throws Exception { Iterator<HandlerMapping> it = this.handlerMappings.iterator(); while (it.hasNext()) { HandlerMapping hm = it.next(); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'"); } return hm.getHandler(request); } return null; } private String getDefaultViewName(HttpServletRequest request) { String url = request.getServletPath(); url = url.replaceAll("/", ""); url = url.replaceAll(".html", ""); url = url.replaceAll(".htm", ""); url = "WEB-INF/" + url + ".jsp"; return url; } }

初始化操作.

  1. 檢查係統中是否已經定義HandlerMapping。如果沒有定義,則使用默認配置。
  2. 檢查係統中是否已經定義HandlerAdapter。如果沒有定義,則使用默認配置。
  3. 檢查係統中是否已經定義ViewResolover。如果沒有定義,則使用默認配置。

請求處理.

  1. 根據特定的請求,使用HandlerMapping找到相應的控製器Handler。
  2. 找到支持此種handler的HandlerAdapter,handler處理完響應業務後,HandlerAdapter把它轉化為ModelAndView對象。
  3. 利用ViewResolver對ModelAndView進行分析,生成相應的View對象。
  4. 生成響應。

默認配置 com.google.mvc.web.servlet.HandlerMapping=com.google.mvc.web.servlet.handler.URLHandlerMapping com.google.mvc.web.servlet.HandlerAdapter=com.google.mvc.web.servlet.mvc.HttpRequestHandlerAdapter,/ com.google.mvc.web.servlet.mvc.ControllerHandlerAdapter com.google.mvc.web.servlet.ViewResolver=com.google.mvc.web.servlet.mvc.DefaultViewResolver

相關文章:

  1. MVC架構探究及其源碼實現(1)-理論基礎
  2. MVC架構探究及其源碼實現(2)-核心組件定義
  3. MVC架構探究及其源碼實現(3)-WebApplicationContext
  4. MVC架構探究及其源碼實現(5)-相關組件實現
  5. MVC架構探究及其源碼實現(6)-簡單示例

最後更新:2017-04-02 04:01:42

  上一篇:go MVC架構探究及其源碼實現(5)-相關組件實現
  下一篇:go 什麼是xp極限編程