新建的springmvc项目上有个红叉_阅读SpringMVC源码前,不妨看下简易版本SpringMVC框架的搭建

开发环境: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项目

d2049dc8ab16fad5a762dbaef98ad1fc.png

新建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.

最终还是要展示下效果

03b502c8a13aef2775bc0b8a3ce59bed.png

94ab15f3ee4983625051176d735fef0e.png

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

项目基本结构图:

59ea21554e608fe5af0b0c8a164a7f70.png

别人家的SpringMVC实现原理图:

2156b45f73b5abe65b4d68d4d33b824d.png

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

--|END|--

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

12d9a2e16e4ba0a05cf8ae5141bd9757.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值