开发环境:windows10、idea、jdk1.8、apache-tomcat-9.0.0.M3

SpringMVC框架是基于Servlet设计的,所以如果你知道SpringMVC,但是没听过道Servlet,那你就应该先去学习下Servlet的知识点了。以下我所描述的SpringMVC框架都是简易版本SpringMVC框架。该框架主要是以请求为驱动,其核心是DispatcherServlet类,它实际上就是一个Servlet,底层实现的也是Servlet。在SpringMVC框架中DispatcherServlet主要做两件事,

第一 :处理请求路径到各个Handler之间的映射,这里的Handler就类似我们平时使用的Controller,也就是struts里边的action,这时候需要使用一个HandlerMapping的东西,也就是一个map来保存映射关系,并且需要在初始化的时候就保存,这就使用到了Servlet生命周期中初始化阶段所调用的init()方法;

第二 :将用户请求分发到各个具体的Handler,并且将请求交给Handler中的具体方法去处理,然后就接收返回回来的ModelAndView,查询一个或多个ViewResoler视图解析器,找到ModelAndView指定的视图,视图负责将结果显示到客户端。

首先我们先新建一个Java Enterprise项目,也就是一个java web项目

新建JavaWeb项目

项目建立好之后,在web.xml中我们先做简单的配置,将客户端所有的请求交给DispatcherServlet这个类去处理。

<?xml version="1.0" encoding="UTF-8"?>dispatcherServletcom.zhainan.springmvc.servlet.DispatcherServletdispatcherServlet/*

接下来就是最为重要的DispatcherServlet实现:

public class DispatcherServlet extends HttpServlet {    private Map handlerBeanMap;    @Override    public void init() throws ServletException {        handlerBeanMap = new HashMap();        for (String name : ClassUtils.getAllClassesFromPackage("com.zhainan.springmvc.handler")) {            try {                Class clz = Class.forName(name);                for (Method method : clz.getDeclaredMethods()) {                    RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);                    if (requestMapping != null) {                        String path = requestMapping.value();                        handlerBeanMap.put(path, new HandlerBean(name, method.getName()));                    }                }            } catch (ClassNotFoundException e) {                e.printStackTrace();            }        }    }    @Override    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {        doDispatch(req, resp);    }    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws IOException    {        String path = req.getPathInfo();        HandlerBean handlerBean = handlerBeanMap.get(path);        if (handlerBean == null)            handlerBean = new HandlerBean("com.zhainan.springmvc.handler.DefaultHandler", "doService");        try {            Class clz = Class.forName(handlerBean.getBeanName());            for (Method method : clz.getDeclaredMethods()) {                if (handlerBean.getMethodName().equals(method.getName())) {                    Parameter[] parameters = method.getParameters();                    Object[] args = new Object[parameters.length];                    for (int i = 0; i < parameters.length; i++) {                        RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);                        if (requestParam != null) {                            String parameterName = requestParam.value();                            args[i] = req.getParameter(parameterName);                        }                        if (parameters[i].getType() == HttpServletResponse.class) {                            args[i] = resp;                        }                    }                    ModelAndView view = (ModelAndView) method.invoke(clz.newInstance(), args);                    new ViewResolver().render(view, req, resp);                }            }        } catch (ClassNotFoundException e) {            e.printStackTrace();        } catch (InvocationTargetException e) {            e.printStackTrace();        } catch (InstantiationException e) {            e.printStackTrace();        } catch (IllegalAccessException e) {            e.printStackTrace();        }    }}

可以看到简易SpringMVC框架中的DispatcherServlet直接继承自HttpServlet,重写了它的init()方法和doGet()方法,init()方法先保存请求路径到各个Handler之间的映射,当然这里边就涉及了类ClassUtils中的一个重要方法和一个类HandlerBean,以及一些关键注解

ClassUtils类:根据包名定位到文件夹,然后根据读取该包下所有class文件,最后根据包名和文件名,构造返回的完整类名.

public class ClassUtils {    public static List getAllClassesFromPackage(String packageName){        List classNames = new ArrayList();        String pkg = "com.zhainan.springmvc.handler";        String relPath = pkg.replace('.', '/');        URL resource = Thread.currentThread().getContextClassLoader().getResource(relPath);        if (resource == null) {            throw new RuntimeException("Unexpected problem: No resource for "                    + relPath);        }        File f = new File(resource.getPath());        String[] files = f.list();        for (int i = 0; i < files.length; i++) {            String fileName = files[i];            String className = null;            String fileNm = null;            if (fileName.endsWith(".class")) {                fileNm = fileName.substring(0, fileName.length() - 6);                className = pkg + '.' + fileNm;            }            if (className != null) {                classNames.add(className);            }        }        return classNames;    }}

HandlerBean类:具体Handler和其中处理方法的对应关系

public class HandlerBean {    private String beanName;    private String methodName;    public HandlerBean(String beanName, String methodName) {        this.beanName = beanName;        this.methodName = methodName;    }    public String getBeanName() {        return beanName;    }    public void setBeanName(String beanName) {        this.beanName = beanName;    }    public String getMethodName() {        return methodName;    }    public void setMethodName(String methodName) {        this.methodName = methodName;    }}

注解RequestMapping:和Controller里边的RequestMapping是一样的道理,将请求路径映射到某个具体方法

@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface RequestMapping {    String value() default "";}

注解RequestParam:和Controller里边的RequestParam一样,请求路径中的请求参数接收

@Target(ElementType.PARAMETER)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface RequestParam {    String value() default "";}

注解PathVariable:和Controller里边的PathVariable一样,请求路径中的路径变量接收

@Target(ElementType.PARAMETER)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface PathVariable {    String value() default "";}

doGet()方法直接调用类里边的doDispatch()方法用于将用户请求分发到各个具体的Handler去处理。接收返回回来的ModelAndView,查询一个或多个ViewResoler视图解析器,找到ModelAndView指定的视图,视图负责将结果显示到客户端。

首先来看看我们的Handler实现,在Demo中我定义了两个Handler

DefaultHandler:用于处理默认请求

public class DefaultHandler {    public void doService(HttpServletRequest request,                          HttpServletResponse response) throws IOException {        response.getWriter().write("this is index page !");    }}

DemoHandler:用于处理指定路径的请求,可以在这编写相关的API

public class DemoHandler {    @RequestMapping(value = "/test")    public void test(HttpServletRequest request,                     HttpServletResponse response) throws IOException {        response.getWriter().write("

test

"); } @RequestMapping(value = "/demo") public ModelAndView demo(@RequestParam(value = "name") String name, @RequestParam(value = "age") String age) { Map model = new HashMap(); model.put("name", name); model.put("age", age); return new ModelAndView(model, "my.view"); }}

除此之外,还可以去编写其他的Handler,如同Controller一样去处理各类请求。

接下来就是关于视图的处理了,先来看下ModelAndView

ModelAndView:ModelAndView中包含了模型(Model)和视图(View),Handler处理完具体的业务逻辑后可以返回对应的模型和需要返回的视图

public class ModelAndView {    private Map model;    private String viewName;    public ModelAndView(Map model, String viewName) {        this.model = model;        this.viewName = viewName;    }    public Map getModel() {        return model;    }    public void setModel(Map model) {        this.model = model;    }    public String getViewName() {        return viewName;    }    public void setViewName(String viewName) {        this.viewName = viewName;    }}

然后再编写一个小型的试图解析功能

先定义一个接口View

public interface View {    public void render(ModelAndView view,                       HttpServletRequest request,                       HttpServletResponse response) throws IOException;}

然后定义一个泪ViewResolver去实现View的render()方法

public class ViewResolver implements View {    @Override    public void render(ModelAndView view, HttpServletRequest request, HttpServletResponse response) throws IOException {        PrintWriter writer = response.getWriter();        String viewName = view.getViewName();        String content = loadTemplate(request.getServletContext().getResourceAsStream("/WEB-INF/" + viewName));        content = parseTemplate(view.getModel(), content);        writer.write(content);    }    private String loadTemplate(InputStream is) throws IOException {        BufferedReader reader = new BufferedReader(new InputStreamReader(is));        StringBuilder content = new StringBuilder();        String line = null;        while ((line = reader.readLine()) != null) {            content.append(line);        }        return content.toString();    }    private String parseTemplate(Map model, String template) {        for (Map.Entry entry : model.entrySet()) {            String key = entry.getKey();            String matchedKey = String.format("${%s}", key);            template = template.replaceAll(matchedKey, entry.getValue());        }        return template;    }}

最后就是视图文件了,demo中定义了一个简单的视图文件my.view

   test

hello,my name is ${name},${age} years old.

最终还是要展示下效果

附上项目基本结构图和别人家的SpringMVC实现原理图帮助理解

项目基本结构图:

别人家的SpringMVC实现原理图:

项目Git地址:https://gitee.com/lvchang/springmvc_demo.git

--|END|--

欢迎搜索个人微信公众号“宅男一号”加入宅基地,给你带来更多IT内容分享!

ant指定servlet版本_阅读SpringMVC源码前,不妨看下简易版本SpringMVC框架的搭建相关推荐

  1. webuploader 怎么在react中_另辟蹊径搭建阅读React源码调试环境支持所有React版本细分文件断点调试...

    引言(为什么写这篇文章) 若要高效阅读和理解React源码,搭建调试环境是必不可少的一步.而常规方法:使用react.development.js和react-dom.development.js调试 ...

  2. Flask源码解析:从第一个版本开始阅读Flask源码

    本项目是<Flask Web开发实战>的衍生品.在本书第16章的前半部分,为了让读者快速对Flask的源码结构建立一个初步的认识(以便阅读后面的内容),推荐读者阅读0.1版本的源码. 本项 ...

  3. zookeeper 密码_「附源码」Dubbo+Zookeeper 的 RPC 远程调用框架

    技术博文,及时送达 作者 | 码农云帆哥 链接 | blog.csdn.net/sinat_27933301 上一篇:从零搭建创业公司后台技术栈 这是一个基于Dubbo+Zookeeper 的 RPC ...

  4. 大数据之-Hadoop源码编译_编译hadoop源码前_需要准备的软件_以及编译步骤---大数据之hadoop工作笔记0045

    编译源码,首先要准备一台Centos可以联网的虚拟机,一定要可以连接外网 然后准备上面的这些软件. 可以看到软件都在上面这个目录下. 首先是安装jdk,然后配置JAVA_HOME

  5. [源码解析] NVIDIA HugeCTR,GPU版本参数服务器--- (5) 嵌入式hash表

    [源码解析] NVIDIA HugeCTR,GPU版本参数服务器- (5) 嵌入式hash表 文章目录 [源码解析] NVIDIA HugeCTR,GPU版本参数服务器--- (5) 嵌入式hash表 ...

  6. 如何阅读Spring源码

    如何阅读Spring源码 如果你是一名JAVA开发人员,你一定用过Spring Framework. 作为一款非常经典的开源框架,从2004年发布的1.0版本到现在的5.0版本,已经经历了14年的洗礼 ...

  7. codeblock socket 编译错误_从Linux源码看Socket(TCP)Client端的Connect

    从Linux源码看Socket(TCP)Client端的Connect 前言 笔者一直觉得如果能知道从应用到框架再到操作系统的每一处代码,是一件Exciting的事情. 今天笔者就来从Linux源码的 ...

  8. 简单直接让你也读懂springmvc源码分析(3.1)-- HandlerMethodReturnValueHandler

    该源码分析系列文章分如下章节: springmvc源码分析(1)-- DispatcherServlet springmvc源码分析(2)-- HandlerMapping springmvc源码分析 ...

  9. SpringMVC源码阅读系列汇总

    1.前言 1.1 导入 SpringMVC是基于Servlet和Spring框架设计的Web框架,做JavaWeb的同学应该都知道 本文基于Spring4.3.7源码分析,(不要被图片欺骗了,手动滑稽 ...

最新文章

  1. 9中继器添加一列序号自增_三个动态自动更新EXCEL序号的小技巧,解决重复编号困扰...
  2. Java数据库篇3——SQL
  3. java 指定垃g1圾收集_Java知识梳理--JVM
  4. select2搜索动态加载
  5. java xml 表达式语言_中级Java开发工程师笔试题
  6. Unity UGUI——UI基础,Canvas
  7. 颜值即正义!颜值爆表的几个数据交互的库来啦!
  8. vs code react-native 安卓调试_实战|C++在vscode上的调试配置
  9. linux tail 命令
  10. 星光商务2008辉煌版 单机版 bt
  11. 嵌入式Linux学习笔记
  12. 清理win7系统盘空间
  13. 阿里云免费SSL证书申请与安装使用-附Nginx,Apache,IIS 6,IIS 8配置SSL教程
  14. 2022届互联网校招薪资开奖,拼多多最高年薪 75 万!
  15. js控制html控件显示隐藏和是否可用
  16. 虚拟机迁移技术漫谈(转)
  17. Jetson Nano 系列之:刷机、瘦身、部署应用
  18. Java程序员的认证--SUN认证
  19. graphpad两组t检验_SPSS如何比较样本两组样本的组内和组间差异(含GraphPad Prism绘图)...
  20. Windows Nginx 服务器 SSL 证书安装部署

热门文章

  1. [译] APT分析报告:04.Kraken - 新型无文件APT攻击利用Windows错误报告服务逃避检测
  2. 【学习排序】 Learning to Rank中Pointwise关于PRank算法源码实现
  3. 【数据结构与算法】之深入解析“课程表II”的求解思路与算法示例
  4. 蚂蚁集团董事调整:黄益平等五人新增,彭蕾等三人退出
  5. ALGO-1 区间k大数查询
  6. BASIC-1 闰年判断
  7. 【Qt】水平和垂直布局
  8. 【Android】Android中WIFI开发总结(一)
  9. mysql工_mysql
  10. C++学习笔记-----二分法之寻找非减序列第一个大于某个值的数或最后一个小于某个值的数