开发环境: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](https://i-blog.csdnimg.cn/blog_migrate/d16e0765fb23e3cf15f1f5ba6c237850.jpeg)
新建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](https://i-blog.csdnimg.cn/blog_migrate/095e1095f8bc8120b030074cb7043835.jpeg)
![94ab15f3ee4983625051176d735fef0e.png](https://i-blog.csdnimg.cn/blog_migrate/9a2e4fac4e63a113f49bd0a4f0649f54.jpeg)
附上项目基本结构图和别人家的SpringMVC实现原理图帮助理解
项目基本结构图:
![59ea21554e608fe5af0b0c8a164a7f70.png](https://i-blog.csdnimg.cn/blog_migrate/8f76a715ffacfaf6c00030f39e1ab5ff.jpeg)
别人家的SpringMVC实现原理图:
![2156b45f73b5abe65b4d68d4d33b824d.png](https://i-blog.csdnimg.cn/blog_migrate/3d16ba3ae503ecff1f80b2a80634d6ed.jpeg)
项目Git地址:https://gitee.com/lvchang/springmvc_demo.git
--|END|--
欢迎搜索个人微信公众号“宅男一号”加入宅基地,给你带来更多IT内容分享!
![12d9a2e16e4ba0a05cf8ae5141bd9757.png](https://i-blog.csdnimg.cn/blog_migrate/8bcde791bc420feb29defd789d4dad79.jpeg)