MVC架构探究及其源码实现(4)-前端控制器

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

前端控制器是整个MVC框架中最为核心的一块,它主要用来拦截符合要求的外部请求,并把请求分发到不同的控制器去处理,根据控制器处理后的结果,生成相应的响应发送到客户端。前端控制器既可以使用Filter实现(Struts2采用这种方式),也可以使用Servlet来实现。这里我们就采用后一种方式来实现我们的MVC框架。

 MVC架构探究及其源码实现(4)-前端控制器

1.配置web.xml,使得我们的前端控制器可以拦截所有符合要求的用户请求,这里我们的前端控制器能处理所有以.do结尾的用户请求。 <?xml version="1.0" encoding="ISO-8859-1"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://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)-简单示例

上一篇:[译] ASP.NET 生命周期 – ASP.NET 请求生命周期(四)


下一篇:Linux字符设备驱动