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天前
|
存储 开发框架 前端开发
前端框架EXT.NET Dotnet 3.5开发的实验室信息管理系统(LIMS)成品源码 B/S架构
发展历史:实验室信息管理系统(LIMS),就是指通过计算机网络技术对实验的各种信息进行管理的计算机软、硬件系统。也就是将计算机网络技术与现代的管理思想有机结合,利用数据处理技术、海量数据存储技术、宽带传输网络技术、自动化仪器分析技术,来对实验室的信息管理和质量控制等进行全方位管理的计算机软、硬件系统,以满足实验室管理上的各种目标(计划、控制、执行)。
20 1
|
1天前
|
安全 数据管理 中间件
云LIS系统源码JavaScript+B/S架构MVC+SQLSugar医院版检验科云LIS系统源码 可提供演示
检验科云LIS系统源码是医疗机构信息化发展的重要趋势。通过云计算技术实现数据的集中管理和共享可以提高数据利用效率和安全性;通过高效灵活的系统设计和可扩展性可以满足不同医疗机构的需求;通过移动性和智能化可以提高医疗服务的精准度和效率;通过集成性可以实现医疗服务的协同性和效率。因此,多医院版检验科云LIS系统源码将成为未来医疗机构信息化发展的重要方向之一。
27 2
|
1天前
|
前端开发 Java 数据库
MVC架构简述
MVC架构简述
11 4
|
1天前
|
前端开发 JavaScript 安全
【网络安全】WebPack源码(前端源码)泄露 + jsmap文件还原
【网络安全】WebPack源码(前端源码)泄露 + jsmap文件还原
15 0
|
1天前
|
存储 安全 前端开发
PHP医院安全不良事件管理系统源码(AEMS)前端vue2+element+后端laravel8不良事件上报与闭环管理
医院不良事件上报与管理系统结合现代医院管理思路,遵照PDCA全面质量循环管理方法而设计,并在多家大型三甲医院成熟运用。系统从事件上报、基于人、机、料、法 、环的RCA分析、事件整改、效果评估实现了结构化、标准化、智能化的管理和分析,满足医院可追溯化、全流程闭环管理要求,满足等级医院评审细则要求,大力提高医院不良事件上报的效率,保障事件分析的准确性,促进医疗安全的提高,避免同类事件再次发生,改善整个医院医疗安全,从而实现医院安全医疗的目标。
23 3
|
1天前
|
前端开发 JavaScript 安全
【TypeScript技术专栏】TypeScript在微前端架构中的应用
【4月更文挑战第30天】微前端架构通过拆分应用提升开发效率和降低维护成本,TypeScript作为静态类型语言,以其类型安全、代码智能提示和重构支持强化这一架构。在实践中,TypeScript定义公共接口确保跨微前端通信一致性,用于编写微前端以保证代码质量,且能无缝集成到构建流程中。在微前端架构中,TypeScript是保障正确性和可维护性的有力工具。
|
1天前
|
前端开发 开发者
【专栏】BEM(Block-Element-Modifier)是一种前端命名规范和架构方法,旨在创建清晰、可维护的代码结构。
【4月更文挑战第29天】BEM(Block-Element-Modifier)是一种前端命名规范和架构方法,旨在创建清晰、可维护的代码结构。它包括Block(独立功能单元)、Element(Block的子元素)和Modifier(表示状态或变体)。BEM的特点包括命名一致性、模块化设计、清晰结构和可复用性,适用于代码组织、样式管理、组件化开发和团队协作。虽然命名较长和学习成本是其局限性,但BEM在提升代码质量和效率方面具有显著优势,是前端开发的重要工具。
|
1天前
|
存储 前端开发 Java
软件体系结构 - 架构风格(13)MVC架构风格
【4月更文挑战第21天】软件体系结构 - 架构风格(13)MVC架构风格
30 0
|
1天前
|
JSON 前端开发 JavaScript
【2024-04-22 源码】最新PDF批注注释插件库,pdf.js插件库,纯前端离线JavaScript库(PDF高亮、下划线、橡皮擦、文本框、画笔、历史记录)
一款基于 pdf.js 开发的PDF批注插件库,支持纯离线内网部署,功能完善、强大且在不断升级,极易上手,欢迎关注!
36 4
【2024-04-22 源码】最新PDF批注注释插件库,pdf.js插件库,纯前端离线JavaScript库(PDF高亮、下划线、橡皮擦、文本框、画笔、历史记录)
|
1天前
|
前端开发 Java PHP
信息系统架构模型(1) MVC
信息系统架构模型(1) MVC
23 0