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="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)-简单示例

目录
相关文章
|
1月前
|
数据库 Android开发 开发者
构建高性能微服务架构:从理论到实践构建高效Android应用:探究Kotlin协程的优势
【2月更文挑战第16天】 在当今快速迭代和竞争激烈的软件市场中,微服务架构以其灵活性、可扩展性和独立部署能力而受到企业的青睐。本文将深入探讨如何构建一个高性能的微服务系统,涵盖从理论基础到具体实现的各个方面。我们将重点讨论服务拆分策略、通信机制、数据一致性以及性能优化等关键主题,为读者提供一个清晰、实用的指南,以便在复杂多变的业务环境中构建和维护健壮的微服务体系结构。 【2月更文挑战第16天】 在移动开发领域,性能优化和流畅的用户体验是至关重要的。随着技术的不断进步,Kotlin作为一种现代编程语言,在Android开发中被广泛采用,尤其是其协程特性为异步编程带来了革命性的改进。本文旨在深入
237 5
|
2月前
|
监控 数据可视化 关系型数据库
微服务架构+Java+Spring Cloud +UniApp +MySql智慧工地系统源码
项目管理:项目名称、施工单位名称、项目地址、项目地址、总造价、总面积、施工准可证、开工日期、计划竣工日期、项目状态等。
304 6
|
1月前
|
存储 前端开发 BI
基于云计算技术的B/S架构智能云HIS系统源码 集挂号、处方、收费、取药、病历于一体
云HIS是针对中小医院机构、乡镇卫生室推出的一套基于云端的云HIS服务平台,借助云HIS,将医院业务流程化,大大提高医院的服务效率和服务质量,为客户提供医院一体化的信息解决方案。云HIS主要功能:包含门诊收费管理,住院收费管理,门诊医生工作站,住院医生工作站,住院护士工作站,辅助检查科室管理,药房药品管理,药库药品管理,报表查询。满足诊所、中小医院业务中看诊、收费、发药、药库管理、经营分析等多环节的工作需要。
40 4
|
2月前
|
前端开发 Java fastjson
且谈软件架构(二) 模块化与MVC
且谈软件架构(二) 模块化与MVC
|
20天前
|
设计模式 开发框架 编译器
【深入探究Qt内部架构】QObject、事件循环与Q_OBJECT宏的协同作用(二)
【深入探究Qt内部架构】QObject、事件循环与Q_OBJECT宏的协同作用
32 0
|
27天前
|
设计模式 前端开发 数据处理
MVC架构中,控制器和模型之间是如何交互的
MVC架构中,控制器和模型之间是如何交互的
8 0
|
27天前
|
存储 设计模式 前端开发
请解释 Web 应用程序的 MVC(模型-视图-控制器)架构。
【2月更文挑战第26天】【2月更文挑战第89篇】请解释 Web 应用程序的 MVC(模型-视图-控制器)架构。
|
1月前
|
小程序 前端开发 API
快递平台独立版小程序源码|带cps推广营销流量主+前端
快递平台独立版小程序源码|带cps推广营销流量主+前端
43 7
快递平台独立版小程序源码|带cps推广营销流量主+前端
|
2月前
|
监控 安全 物联网
B/S架构智慧工地源码,两个终端:PC后台管理端、APP移动端
智慧工地系统充分利用计算机技术、互联网、物联网、云计算、大数据等新一代信息技术,以PC端,移动端,设备端三位一体的管控方式为企业现场工程管理提供了先进的技术手段。让劳务、设备、物料、安全、环境、能源、资料、计划、质量、视频监控等十大管理环节变得智慧可控。为建设集团、施工企业、政府监管部门等提供一站式工地现场管理信息化解决方案,是一种崭新的工程现场一体化管理模式。
27 0
|
2月前
|
架构师 Java
资深大厂JAVA架构师带你剖析Condition源码
Condition是JUC里面提供于控制线程释放锁, 然后进行等待其他获取锁的线程发送 signal 信号来进行唤醒的工具类.
10 0