博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
手动实现 SpringMVC
阅读量:7176 次
发布时间:2019-06-29

本文共 7217 字,大约阅读时间需要 24 分钟。

前几章我们已经分析了 Spring 的源码并且手动实现了一个 IOC/DI 容器。

这章我们在自己实现的 Spring 框架的基础上实现一个 SpringMVC 框架。

我们自己实现的 Spring 框架的快速入口: 里面的 MySpring 是我们 IOC/DI 框架的源码。

下面我们在这个基础上实现一个 MVC 框架。首先 @Controller,@Service,@Repository 注解已经在我们自己的 Spring 框架里面定义了,可以实现将被这些注解标记的类交给 Spring 管理,并且实现了依赖注入。

先看整体架构

1712167-20190617103913942-187681576.png

因为我们这个 MVC 框架要依赖 IOC/DI 容器,所以我们在 pom 文件里要将自己的 Spring 框架引入进来。

1712167-20190617103928654-2103977636.png

实现 MVC 的整体功能

首先我们定义两个 MVC 专用的注解,RequestMapping 用来做 url 匹配,RequestParam 做参数转换:

package com.paul.annotation;import java.lang.annotation.*;@Target({ElementType.TYPE,ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface RequestMapping {    String value() default "";}
package com.paul.annotation;import java.lang.annotation.*;@Target(ElementType.PARAMETER)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface RequestParam {    String value() default "";}

我们知道 SpringMVC 的核心是 DispatcherServlet,用来做核心路由控制,我们也定义这样一个类,并且在初始化方法里初始化一个 IOC/DI 容器,看过前面文章的同学应该知道,初始化容器后我们已经将 Bean 放到容器中而且完成了依赖注入。

package com.paul.servlet;import com.paul.annotation.RequestMapping;import com.paul.annotation.RequestParam;import org.springframework.ioc.annotation.Controller;import org.springframework.ioc.bean.AnnotationApplicationContext;import org.springframework.ioc.bean.ApplicationContext;import javax.servlet.ServletConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.lang.annotation.Annotation;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.Map;import java.util.concurrent.ConcurrentHashMap;//实现 servlet,和我们以前使用 servlet 一样。public class DispatcherServlet extends HttpServlet {    // 完整路径和 方法的 mapping    private Map
handleMapping = new ConcurrentHashMap<>(); // 类路径和controller 的 mapping private Map
controllerMapping = new ConcurrentHashMap<>(); private Map
beanFactory = new ConcurrentHashMap<>(); @Override public void init(ServletConfig config) throws ServletException { super.init(config); //实例化 IOC 容器 ApplicationContext ctx = new AnnotationApplicationContext("applicationContext.xml"); this.beanFactory = ((AnnotationApplicationContext) ctx).beanFactory; //上一步已经完成了 Controller,service,respostry,autowired 等注解的扫描和注入 //遍历容器,将 requestmapping 注解的路径和对应的方法以及 contoller 实例对应起来 for(Map.Entry
entry:beanFactory.entrySet()){ Object instance = entry.getValue(); Class
clazz = instance.getClass(); if(clazz.isAnnotationPresent(Controller.class)){ RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class); String classPath = requestMapping.value(); Method[] methods = clazz.getMethods(); for(Method method:methods){ if(method.isAnnotationPresent(RequestMapping.class)){ RequestMapping requestMapping2 = method.getAnnotation(RequestMapping.class); String methodPath = requestMapping2.value(); String requestPath = classPath + methodPath; handleMapping.put(requestPath,method); controllerMapping.put(requestPath,instance); }else{ continue; } } }else{ continue; } } } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doPost(req, resp); } //根据上一步获取到的 mapping,根据 url 找到对应的 controller 和方法去执行。 @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String uri = req.getRequestURI(); // /paul-mvc/com.paul.controller/method-com.paul.controller String context = req.getContextPath(); // /paul-vmc String path = uri.replace(context,""); // /com.paul.controller/method-com.paul.controller Method m = (Method) handleMapping.get(path); //从容器里拿到controller 实例 Object instance = controllerMapping.get(path); Object[] args = handle(req,resp,m); for (Object a:args){ System.out.println("Object:"+a); } try { m.invoke(instance,args); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } private static Object[] handle(HttpServletRequest req, HttpServletResponse resp,Method method){ //拿到当前执行的方法有哪些参数 Class
[] paramClazzs = method.getParameterTypes(); //根据参数的个数,new 一个参数的数据 Object[] args = new Object[paramClazzs.length]; int args_i = 0; int index = 0; for(Class
paramClazz:paramClazzs){ if(ServletRequest.class.isAssignableFrom(paramClazz)){ args[args_i++] = req; } if(ServletResponse.class.isAssignableFrom(paramClazz)){ args[args_i++] = resp; } //判断requestParam 注解 Annotation[] paramAns = method.getParameterAnnotations()[index]; if(paramAns.length > 0){ System.out.println("my"); for(Annotation paramAn:paramAns){ if(RequestParam.class.isAssignableFrom(paramAn.getClass())){ System.out.println("13mj"); RequestParam rp = (RequestParam) paramAn; args[args_i++] = req.getParameter(rp.value()); } } } index ++; } return args; }}

测试代码

新建一个 WebDemo,Maven web 项目。

先来看我们需要测试的 Controller 和 Service。

@Controller@RequestMapping("/user")public class UserController {    @Autowired    private UserService userService;    @RequestMapping("/query")    public void get(HttpServletRequest request, HttpServletResponse response, @RequestParam("name") String name,@RequestParam("age") String age){        try {            PrintWriter pw = response.getWriter();            String res = userService.query(name,age);            pw.write(res);        } catch (IOException e) {            e.printStackTrace();        }    }}
import org.springframework.ioc.annotation.Service;@Service("userService")public class UserServiceImpl implements UserService {    @Override    public String query(String name, String age) {        return "name="+name+"age="+age;    }}

在 resources 目录我们需要写一个名字为 applicationContext 的配置文件来指明扫描包路径。

在 web.xml 中配置我们自己的 DispatcherServlet。

Archetype Created Web Application
DispatcherServlet
com.paul.servlet.DispatcherServlet
0
DispatcherServlet
/

在浏览器中测试结果:

1712167-20190617103959962-2013309167.png

结果和我们想的一样。

整个项目的源码在:,希望大家多 star 一起改进。

转载于:https://www.cnblogs.com/paulwang92115/p/11032277.html

你可能感兴趣的文章
我们来一起说说HTTPS中间人攻击与证书校验
查看>>
emacs-win使用指南
查看>>
dblink故障一例
查看>>
基于jQuery左右滑动切换特效 附源码
查看>>
svn 文件批量删除自动提交脚本(python)
查看>>
05.Django表单的使用
查看>>
DPM全方位保护SQL Server,DPM2007系列之五
查看>>
TreeView
查看>>
谈谈制定职业规划的意义
查看>>
PowerPC VxWorks BSP分析(3.1)——POWERQUICC硬件
查看>>
全排列算法原理和实现
查看>>
JavaScript中圆括号()和方括号[]的一个特殊用法
查看>>
在Windows上的Visual Studio中安装Xamarin
查看>>
PostgreSQL 的 语法分析的理解(二)
查看>>
现代软件工程 结对编程 (II) 电梯调度
查看>>
JavaWeb-11 (JSP&amp;EL表达)
查看>>
WebAPI用法
查看>>
【读书】领导力的5个层次-概述
查看>>
windows 80端口被占用的解决方法
查看>>
C# WinForm开发系列 - Regular Expression
查看>>