MVC架构探究及其源码实现(5)-相关组件实现

简介:

博学,切问,近思--詹子知(http://blog.csdn.net/zhiqiangzhan)

本文将讨论HandlerMapping,HandlerAdapter,ViewResolver组件类的具体实现。

URLHandlerMapping,利用request中包含的url信息,找到对应Handler对象,URLHandlerMapping是最典型的映射方式。 package com.google.mvc.web.servlet.handler; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import javax.servlet.http.HttpServletRequest; import org.apache.log4j.Logger; import com.google.mvc.web.context.WebApplicationContext; import com.google.mvc.web.context.WebApplicationContextAware; import com.google.mvc.web.servlet.HandlerMapping; import com.google.mvc.web.servlet.mvc.Controller; import com.google.mvc.web.servlet.mvc.HttpRequestHandler; public class URLHandlerMapping implements HandlerMapping, WebApplicationContextAware{ private static final Logger LOGGER = Logger.getLogger(URLHandlerMapping.class); private WebApplicationContext wac; private Map<String, Object> handlerMap = new ConcurrentHashMap<String, Object>(); private AtomicBoolean initialize = new AtomicBoolean(false); @Override public void setWebApplicationContext(WebApplicationContext wac) { this.wac = wac; } @Override public Object getHandler(HttpServletRequest request) throws Exception { if(LOGGER.isDebugEnabled()){ LOGGER.debug("Find handler for request " + request.getServletPath()); } if(initialize.compareAndSet(false, true)){ Map<String, HttpRequestHandler> map1 = wac.beansOfType(HttpRequestHandler.class); for(String key : map1.keySet()){ handlerMap.put(key, map1.get(key)); } Map<String, Controller> map2 = wac.beansOfType(Controller.class); for(String key : map2.keySet()){ handlerMap.put(key, map2.get(key)); } } Object handler = handlerMap.get(getHandlerName(request)); if(handler == null){ handler = handlerMap.get("404"); } return handler; } protected String getHandlerName(HttpServletRequest request){ String path = request.getServletPath(); int index = path.lastIndexOf('/'); String handleName = path.substring(index + 1, path.length()); return handleName; } }

HandlerAdapter用于把不同的Handler对象处理的结果封装成一个统一的对象ModelAndView,以达到逻辑上的一致处理。在这里,我们定义两种Handler类型

  1. Controller:用于处理用户的业务逻辑,并返回具体ModelAndView对象,具体接口定义如下 package com.google.mvc.web.servlet.mvc; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.google.mvc.web.servlet.ModelAndView; public interface Controller { ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception; }
  2. HttpRequestHandler:用于处理简单的HTTP请求。接口定义如下 package com.google.mvc.web.servlet.mvc; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public interface HttpRequestHandler { void handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception; } 我们来看一下HandlerFor404的简单实现。 package com.google.mvc.web.servlet.mvc; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class HandlerFor404 implements HttpRequestHandler { @Override public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { response.sendRedirect("404.html"); } }
我们需要为每一种不同的Handler类型指定一种HandlerAdapter,但这不是必须的。
ControllerHandlerAdapter package com.google.mvc.web.servlet.mvc; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.google.mvc.web.servlet.HandlerAdapter; import com.google.mvc.web.servlet.ModelAndView; public class ControllerHandlerAdapter implements HandlerAdapter{ public boolean supports(Object handler) { return (handler instanceof Controller); } public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return ((Controller) handler).handleRequest(request, response); } public long getLastModified(HttpServletRequest request, Object handler) { return -1L; } } HttpRequestHandlerAdapter package com.google.mvc.web.servlet.mvc; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.google.mvc.web.servlet.HandlerAdapter; import com.google.mvc.web.servlet.ModelAndView; public class HttpRequestHandlerAdapter implements HandlerAdapter { @Override public long getLastModified(HttpServletRequest request, Object handler) { return 0; } @Override public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { ((HttpRequestHandler) handler).handleRequest(request, response); return null; } @Override public boolean supports(Object handler) { return (handler instanceof HttpRequestHandler); } }

ViewResolver用于指定View的生成方式,我们先来看下AbstractView的定义 package com.google.mvc.web.servlet.mvc; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.log4j.Logger; import com.google.mvc.web.servlet.View; public abstract class AbstractView implements View { private static final Logger LOGGER = Logger.getLogger(AbstractView.class); public static final String INCLUDE_REQUEST_URI_ATTRIBUTE = "javax.servlet.include.request_uri"; public static final String FORWARD_REQUEST_URI_ATTRIBUTE = "javax.servlet.forward.request_uri"; public static final String FORWARD_CONTEXT_PATH_ATTRIBUTE = "javax.servlet.forward.context_path"; public static final String FORWARD_SERVLET_PATH_ATTRIBUTE = "javax.servlet.forward.servlet_path"; public static final String FORWARD_PATH_INFO_ATTRIBUTE = "javax.servlet.forward.path_info"; public static final String FORWARD_QUERY_STRING_ATTRIBUTE = "javax.servlet.forward.query_string"; public static final String DEFAULT_CONTENT_TYPE = "text/html;charset=ISO-8859-1"; private String contentType = DEFAULT_CONTENT_TYPE; private boolean alwaysInclude = false; @Override public String getContentType() { return contentType; } protected void exposeModelAsRequestAttributes(Map<String, Object> model, HttpServletRequest request){ Iterator<Entry<String, Object>> it = model.entrySet().iterator(); while (it.hasNext()) { Entry<String, Object> entry = it.next(); String modelName = entry.getKey(); Object modelValue = entry.getValue(); if (modelValue != null) { request.setAttribute(modelName, modelValue); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Added model object '" + modelName + "' of type [" + modelValue.getClass().getName() + "] to request in view with name '" + this + "'"); } } else { request.removeAttribute(modelName); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Removed model object '" + modelName + "' from request in view with name '" + this + "'"); } } } } protected boolean useInclude(HttpServletRequest request, HttpServletResponse response) { return (this.alwaysInclude || request.getAttribute(INCLUDE_REQUEST_URI_ATTRIBUTE) != null || response .isCommitted()); } protected void exposeForwardRequestAttributes(HttpServletRequest request) { exposeRequestAttributeIfNotPresent(request, FORWARD_REQUEST_URI_ATTRIBUTE, request.getRequestURI()); exposeRequestAttributeIfNotPresent(request, FORWARD_CONTEXT_PATH_ATTRIBUTE, request.getContextPath()); exposeRequestAttributeIfNotPresent(request, FORWARD_SERVLET_PATH_ATTRIBUTE, request.getServletPath()); exposeRequestAttributeIfNotPresent(request, FORWARD_PATH_INFO_ATTRIBUTE, request.getPathInfo()); exposeRequestAttributeIfNotPresent(request, FORWARD_QUERY_STRING_ATTRIBUTE, request.getQueryString()); } private void exposeRequestAttributeIfNotPresent(ServletRequest request, String name, Object value) { if (request.getAttribute(name) == null) { request.setAttribute(name, value); } } public void setContentType(String contentType) { this.contentType = contentType; } public boolean isAlwaysInclude() { return alwaysInclude; } public void setAlwaysInclude(boolean alwaysInclude) { this.alwaysInclude = alwaysInclude; } } 在这里我们仅实现一种View类型,也就是对jsp页面的简单处理。 package com.google.mvc.web.servlet.mvc; import java.io.IOException; import java.util.Map; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.log4j.Logger; public class InternalResourceView extends AbstractView { private static final Logger LOGGER = Logger.getLogger(InternalResourceView.class); private String url; @Override public void render(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Rendering view with name '" + this + "' with model " + model); } if(model != null){ exposeModelAsRequestAttributes(model, request); } RequestDispatcher rd = request.getRequestDispatcher(url); if (rd == null) { throw new ServletException("Could not get RequestDispatcher for [" + getUrl() + "]: check that this file exists within your WAR"); } if (useInclude(request, response)) { response.setContentType(getContentType()); rd.include(request, response); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Included resource [" + getUrl() + "] in InternalResourceView '" + url + "'"); } }else { exposeForwardRequestAttributes(request); rd.forward(request, response); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Forwarded to resource [" + getUrl() + "] in InternalResourceView '" + url + "'"); } } } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String toString(){ return this.url; } } 好了,到这里,我们再来看看ViewResolver类的实现 package com.google.mvc.web.servlet.mvc; import org.apache.log4j.Logger; import com.google.mvc.web.servlet.View; import com.google.mvc.web.servlet.ViewResolver; public class DefaultViewResolver implements ViewResolver { private static final Logger LOGGER = Logger.getLogger(DefaultViewResolver.class); private String prefix = ""; private String suffix = ""; private Class<View> viewClass; @Override public View resolveViewName(String viewName) throws Exception { View view = viewClass.newInstance(); if(view instanceof InternalResourceView){ ((InternalResourceView)view).setUrl(prefix + viewName + suffix); } return view; } public void setViewClass(String viewClass){ try { this.viewClass = (Class<View>) this.getClass().getClassLoader().loadClass(viewClass); } catch (ClassNotFoundException e) { LOGGER.error("Can't load view class " + viewClass, e); } } public String getPrefix() { return prefix; } public void setPrefix(String prefix) { this.prefix = prefix; } public String getSuffix() { return suffix; } public void setSuffix(String suffix) { this.suffix = suffix; } }

到这里,我们在整个MVC架构的源码实现已经完成了,下一篇,我们将介绍一个基于我们这个MVC架构的Demo。

相关文章:

  1. MVC架构探究及其源码实现(1)-理论基础
  2. MVC架构探究及其源码实现(2)-核心组件定义
  3. MVC架构探究及其源码实现(3)-WebApplicationContext
  4. MVC架构探究及其源码实现(4)-前端控制器
  5. MVC架构探究及其源码实现(6)-简单示例
目录
相关文章
|
3月前
|
JSON JavaScript 前端开发
Vue3源码架构简析及Monorepo流程构建
【10月更文挑战第12天】Vue3源码架构简析及Monorepo流程构建
Vue3源码架构简析及Monorepo流程构建
|
2月前
|
存储 前端开发 调度
Flux 与传统的 MVC 架构模式区别
Flux是一种用于构建用户界面的架构模式,与传统的MVC架构不同,它采用单向数据流,通过Dispatcher统一管理数据的分发,Store负责存储数据和业务逻辑,View只负责展示数据,使得应用状态更加可预测和易于维护。
|
2月前
|
数据采集 监控 前端开发
二级公立医院绩效考核系统源码,B/S架构,前后端分别基于Spring Boot和Avue框架
医院绩效管理系统通过与HIS系统的无缝对接,实现数据网络化采集、评价结果透明化管理及奖金分配自动化生成。系统涵盖科室和个人绩效考核、医疗质量考核、数据采集、绩效工资核算、收支核算、工作量统计、单项奖惩等功能,提升绩效评估的全面性、准确性和公正性。技术栈采用B/S架构,前后端分别基于Spring Boot和Avue框架。
|
3月前
|
消息中间件 存储 Java
RocketMQ(一):消息中间件缘起,一览整体架构及核心组件
【10月更文挑战第15天】本文介绍了消息中间件的基本概念和特点,重点解析了RocketMQ的整体架构和核心组件。消息中间件如RocketMQ、RabbitMQ、Kafka等,具备异步通信、持久化、削峰填谷、系统解耦等特点,适用于分布式系统。RocketMQ的架构包括NameServer、Broker、Producer、Consumer等组件,通过这些组件实现消息的生产、存储和消费。文章还提供了Spring Boot快速上手RocketMQ的示例代码,帮助读者快速入门。
|
2月前
|
监控 前端开发 数据可视化
3D架构图软件 iCraft Editor 正式发布 @icraft/player-react 前端组件, 轻松嵌入3D架构图到您的项目,实现数字孪生
@icraft/player-react 是 iCraft Editor 推出的 React 组件库,旨在简化3D数字孪生场景的前端集成。它支持零配置快速接入、自定义插件、丰富的事件和方法、动画控制及实时数据接入,帮助开发者轻松实现3D场景与React项目的无缝融合。
164 8
3D架构图软件 iCraft Editor 正式发布 @icraft/player-react 前端组件, 轻松嵌入3D架构图到您的项目,实现数字孪生
|
16天前
|
机器学习/深度学习 前端开发 算法
婚恋交友系统平台 相亲交友平台系统 婚恋交友系统APP 婚恋系统源码 婚恋交友平台开发流程 婚恋交友系统架构设计 婚恋交友系统前端/后端开发 婚恋交友系统匹配推荐算法优化
婚恋交友系统平台通过线上互动帮助单身男女找到合适伴侣,提供用户注册、个人资料填写、匹配推荐、实时聊天、社区互动等功能。开发流程包括需求分析、技术选型、系统架构设计、功能实现、测试优化和上线运维。匹配推荐算法优化是核心,通过用户行为数据分析和机器学习提高匹配准确性。
52 3
|
2月前
|
SQL 数据采集 分布式计算
【赵渝强老师】基于大数据组件的平台架构
本文介绍了大数据平台的总体架构及各层的功能。大数据平台架构分为五层:数据源层、数据采集层、大数据平台层、数据仓库层和应用层。其中,大数据平台层为核心,负责数据的存储和计算,支持离线和实时数据处理。数据仓库层则基于大数据平台构建数据模型,应用层则利用这些模型实现具体的应用场景。文中还提供了Lambda和Kappa架构的视频讲解。
235 3
【赵渝强老师】基于大数据组件的平台架构
|
2月前
|
存储 前端开发 数据可视化
在实际项目中,如何选择使用 Flux 架构或传统的 MVC 架构
在实际项目中选择使用Flux架构或传统MVC架构时,需考虑项目复杂度、团队熟悉度和性能需求。Flux适合大型、高并发应用,MVC则适用于中小型、逻辑简单的项目。
|
3月前
|
SQL 存储 分布式计算
大数据-157 Apache Kylin 背景 历程 特点 场景 架构 组件 详解
大数据-157 Apache Kylin 背景 历程 特点 场景 架构 组件 详解
48 9
|
3月前
|
Java Spring
Spring底层架构源码解析(三)
Spring底层架构源码解析(三)
188 5

热门文章

最新文章