手动实现 SpringMVC
前几章我们已经分析了 Spring 的源码并且手动实现了一个 IOC/DI 容器。
这章我们在自己实现的 Spring 框架的基础上实现一个 SpringMVC 框架。
我们自己实现的 Spring 框架的快速入口:Spring 框架 里面的 MySpring 是我们 IOC/DI 框架的源码。
下面我们在这个基础上实现一个 MVC 框架。首先 @Controller,@Service,@Repository 注解已经在我们自己的 Spring 框架里面定义了,可以实现将被这些注解标记的类交给 Spring 管理,并且实现了依赖注入。
先看整体架构

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

实现 MVC 的整体功能
首先我们定义两个 MVC 专用的注解,RequestMapping 用来做 url 匹配,RequestParam 做参数转换:
package com.paul.annotation;
import java.lang.annotation.*;
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestMapping {
    String value() default "";
}
package com.paul.annotation;
import java.lang.annotation.*;
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @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<String,Object> handleMapping = new ConcurrentHashMap<>();
    // 类路径和controller 的 mapping
    private Map<String,Object> controllerMapping = new ConcurrentHashMap<>();
    private Map<String,Object> 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<String,Object> 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 的配置文件来指明扫描包路径。
<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <package-scan component-scan="com.paul.demo" />
</beans>
在 web.xml 中配置我们自己的 DispatcherServlet。
<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
  <display-name>Archetype Created Web Application</display-name>
  <servlet>
    <servlet-name>DispatcherServlet</servlet-name>
    <servlet-class>com.paul.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>0</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>DispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>
在浏览器中测试结果:

结果和我们想的一样。
整个项目的源码在:源码,希望大家多 star 一起改进。
手动实现 SpringMVC的更多相关文章
- 手动搭建SpringMVC报错
		
猜测这个是由于自己在搭建时缺少包造成的,后来将按照自己之前的项目将包补齐,tomcat就不报错了,看来还是要学习maven 这样就不会缺少包了
 - 手码两万余字,SpringMVC 包教包会
		
1. SpringMVC 简介 1.1 Spring Web MVC是什么 Spring Web MVC 是一种基于 Java 的实现了 Web MVC 设计模式的请求驱动类型的轻量级 Web 框架, ...
 - SpringMVC框架——原理及使用
		
核心组件 1. DispatcherServlet:前端控制器器,负责调度其他组件的执行,可降低不同组件之间的耦合性,是Spring MVC 的核心模块. 2. Handler:处理器,完成具体的业务 ...
 - SSM手把手整合教程&测试事务
		
自打来了博客园就一直在看帖,学到了很多知识,打算开始记录的学习到的知识点 今天我来写个整合SpringMVC4 spring4 mybatis3&测试spring事务的教程,如果有误之处,还请 ...
 - ssm回顾笔记(一)
		
这两天来到了农银,这边即将进行的一个项目是将ssh框架的电商项目迁移到springboot+ssm框架上,所以我基本上是三门技术在同时进行学习,当然以前学过ssm,现在只是回顾. spring 注解 ...
 - SSM简单整合教程&测试事务
		
自打来了博客园就一直在看帖,学到了很多知识,打算开始记录的学习到的知识点 今天我来写个整合SpringMVC4 spring4 mybatis3&测试spring事务的教程,如果有误之处,还请 ...
 - Spring学习(二)——使用Gradle构建一个简单的Spring MVC Web应用程序
		
1.新建一个Gradle工程(Project) 在新建工程窗口的左侧中选择 [Gradle],右侧保持默认选择,点击next,模块命名为VelocityDemo. 2.在该工程下新建一个 module ...
 - Spring - RestTemplate执行原理分析
		
什么是RestTemplate Synchronous client to perform HTTP requests, exposing a simple, template method API ...
 - 4-2 Spring MVC框架-01
		
Spring MVC框架-01 Ⅰ.接收客户端请求 1. 关于Spring MVC框架 Spring MVC是基于Spring框架基础之上的 作用: 接收请求,响应结果,处理异常 主要解决了后端服务器 ...
 
随机推荐
- python 整数中1出现的次数
			
把整数转换为字符串 用count计数 # -*- coding:utf- -*- class Solution: def NumberOf1Between1AndN_Solution(self, n) ...
 - python3使用Lxml库操作XPath
			
download address: http://pypi.python.org/pypi/lxml/2.3 lxml is a Pythonic, mature binding for the li ...
 - silverlight,WPF动画终极攻略之会飞的小鸟篇(Blend 4开发)
			
原文:silverlight,WPF动画终极攻略之会飞的小鸟篇(Blend 4开发) 本教程基本涵盖了WPF和silverlight中的各种动画.先上张效果图. 声明下,这个做的不是让大家照搬的,只是 ...
 - 组态DNS、搜索域名和主机名
			
一个.组态DNS和搜索领域 特别配置DNS该文件是/etc/resolv.conf:同时,我们可以ifcfg-eth0网卡的配置和其他配置文件中指定的. 演示样本: [root@instructor ...
 - WPF应用程序内嵌网页
			
原文:WPF应用程序内嵌网页 版权声明:本文为博主原创文章,转载请注明出处. https://blog.csdn.net/shaynerain/article/details/78160984 WPF ...
 - Swift 的 Currying 特性 | SwiftCafe 咖啡时间
			
Currying 也是 Swift 的众多先进特性之一,用一句话说就是将接受多个参数的函数,转变成每次之接受一个参数的调用序列. 上面一句话说得可能大家感觉不是那么清楚,那么没关系,咱们举一个例子来说 ...
 - WPF 绑定父类属性
			
原文:WPF 绑定父类属性 1.绑定父控件的属性. <ContextMenu x:Key="ContextMenuColoum"> <MenuItem Heade ...
 - String 源码分析
			
Java 源码阅读 - String String 类型看起来简单,实际上背后的复杂性基本可以涵盖了整个 Java 设计,涉及到设计模式(不可变对象).缓存(String Pool 的理念).JVM( ...
 - CORS  专题
			
CORS(跨域资源共享,Cross-Origin Resource Sharing)CORS其实出现时间不短了,它在维基百科上的定义是:跨域资源共享(CORS )是一种网络浏览器的技术规范,它为Web ...
 - <input type="image"> 和 <img> 用法区别
			
原文:<input type="image"> 和 <img> 用法区别 w3c定义如下: Image <input type="image ...