手写简易springmvc

为了提高自己对springmvc框架的理解,决定模仿springmvc手写一个简单demo
1.创建servlet项目
新建一个maven项目,选中webapp项目的结构
新建一个maven项目,选中webapp项目的结构
2.下载完成后添加servlet依赖

  <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.0.1</version>
      <scope>provided</scope>
    </dependency>

3.新增自定义servlet类

package com.txxzctest.springmvcdemo;

import javax.servlet.http.HttpServlet;

/**
 * @author Huang
 */
public class MyDispatcherServlet extends HttpServlet{
}

.
4.删掉原有web.xml内容,修改为自己的servlet内容

<?xml version="1.0" encoding="UTF-8"?>

<web-app version="2.4"
         xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
  <servlet>
    <servlet-name>MySpringMVC</servlet-name>
    <servlet-class>com.txxzctest.springmvcdemo.MyDispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>MySpringMVC</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>
</web-app>
        

5.自定义servlet中重写init、doGet、doPost方法

  @Override
    public void init(ServletConfig config) throws ServletException {
        System.out.println("启动成功!");
        super.init(config);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("get方法测试成功!");
        super.doGet(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("post方法测试成功!");
        super.doPost(req, resp);
    }

6.tomcat启动服务
在这里插入图片描述
看到当前servlet项目启动成功
在这里插入图片描述

7.新增三个自定义的注解
NewController(自定义Controller)

package com.txxzctest.springmvcdemo.annotation;

import java.lang.annotation.*;

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NewController {
    /**
     * 表示给controller注册别名
     * @return
     */
    String value() default "";
}

NewRequestMapping(自定义RequestMapping)

package com.txxzctest.springmvcdemo.annotation;

import java.lang.annotation.*;

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NewRequestMapping {
    /**
     * 表示给requestmapping注册别名
     * @return
     */
    String value() default "";
}

8.扫描包下所有类,并加入到类集合里

首先需要找到相关包下的class文件,web.xml里设置包名

 <servlet>
    <servlet-name>MySpringMVC</servlet-name>
    <servlet-class>com.txxzctest.springmvcdemo.MyDispatcherServlet</servlet-class>
    <init-param>
      <param-name>packName</param-name>
      <param-value>com.txxzctest.springmvcdemo</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>`

修改servlet中启动的逻辑,让其启动时扫描包下所有class文件

//类集合
    private List<String> CLASSNAME = new ArrayList<>();
 //包名
    private String packName;

    @Override
    public void init(ServletConfig config) throws ServletException {
        System.out.println("启动成功!");
        //获取包位置
        packName = config.getInitParameter("packName");
        //扫描包下所有class文件
        doScan(packName);
    }

    private void doScan(String packageName) {
        URL url = this.getClass().getClassLoader().getResource("/"+packageName.replaceAll("\\.", "/"));
        File dir = new File(url.getFile());
        for (File file : dir.listFiles()) {
            if(file.isDirectory()){
                //递归读取包
                doScan(packageName+"."+file.getName());
            }else{
                String className =packageName +"." +file.getName().replace(".class", "");
                //加入到集合中
                CLASSNAME.add(className);
            }
        }
    }

9.装载自定义controller到自定义ioc中

  //ioc容器
    private Map<String, Object> IOC = new HashMap<>();

    @Override
    public void init(ServletConfig config) throws ServletException {
        System.out.println("启动成功!");
        //获取包位置
        packName = config.getInitParameter("packName");
        //扫描包下所有class文件
        doScan(packName);
        //装载实例到ioc
        doInstant();

    }

    private void doInstant() {
        if (CLASSNAME.isEmpty()) {
            return;
        }
        for (String className : CLASSNAME) {
            try {
                Class<?> clazz = Class.forName(className);
                if (clazz.isAnnotationPresent(NewController.class)) {
                    IOC.put(toLowerFirstWord(clazz.getSimpleName()), clazz.newInstance());
                } else {
                    continue;
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 把字符串的首字母小写
     *
     * @param name
     * @return
     */
    private String toLowerFirstWord(String name) {
        char[] charArray = name.toCharArray();
        charArray[0] += 32;
        return String.valueOf(charArray);
    }

10.初始化请求路由处理器

 //url-controller map方便后面访问时使用反射
    private Map<String, Object> controllerMap = new HashMap<>();
    //url-method map方便后面访问时使用反射
    private Map<String, Method> urlMap = new HashMap<>();


    @Override

    public void init(ServletConfig config) throws ServletException {
        System.out.println("启动成功!");
        //获取包位置
        packName = config.getInitParameter("packName");
        //扫描包下所有class文件
        doScan(packName);
        //装载实例到ioc
        doInstant();
        //初始化HandlerMapping(将url和method对应上)
        initHandlerMapping();

    }

    private void initHandlerMapping() {
        if (IOC.isEmpty()) {
            return;
        }
        for (String className : IOC.keySet()) {
            try {
                Class<? extends Object> clazz = IOC.get(className).getClass();
                //不是controller组件跳过
                if (!clazz.isAnnotationPresent(NewController.class)) {
                    continue;
                }
                //获取controller 基本路径
                String controllerUrl = "";
                if(clazz.isAnnotationPresent(NewRequestMapping.class)) {
                    NewRequestMapping annotation = clazz.getAnnotation(NewRequestMapping.class);
                    controllerUrl = annotation.value();
                }
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {
                    if (!method.isAnnotationPresent(NewRequestMapping.class)) {
                        continue;
                    }
                    String methodUrl = method.getAnnotation(NewRequestMapping.class).value();
                    String url = (controllerUrl + "/" + methodUrl).replaceAll("/+", "/");
                    urlMap.put(url, method);
                    controllerMap.put(url, clazz.newInstance());
                    System.out.println(url + "," + method);
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            }
        }
    }

11.重写get和post方法,根据请求路由通过反射执行controller下的对应方法。

 @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doDispath(req, resp);
    }

    private void doDispath(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        if (urlMap.isEmpty()) {
            resp.getWriter().write("404 NOT FOUND!");
            return;
        }

        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        //拼接url并把多个/替换成一个
        url = url.replace(contextPath, "").replaceAll("/+", "/");

        if (!this.urlMap.containsKey(url)) {
            resp.getWriter().write("404 NOT FOUND!");
            return;
        }

        Method method = this.urlMap.get(url);
        //获取方法的参数列表
        Class<?>[] parameterTypes = method.getParameterTypes();

        //获取请求的参数
        Map<String, String[]> parameterMap = req.getParameterMap();

        //保存参数值
        Object[] paramValues = new Object[parameterTypes.length];

        //方法的参数列表
        for (int i = 0; i < parameterTypes.length; i++) {
            //根据参数名称,做某些处理
            String requestParam = parameterTypes[i].getSimpleName();
            if (requestParam.equals("HttpServletRequest")) {
                //参数类型已明确,这边强转类型
                paramValues[i] = req;
                continue;
            }
            if (requestParam.equals("HttpServletResponse")) {
                paramValues[i] = resp;
                continue;
            }
        }
        //利用反射机制来调用
        try {
            method.invoke(this.controllerMap.get(url), paramValues);//obj是method所对应的实例 在ioc容器中
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

编写测试用例

package com.txxzctest.springmvcdemo.controller;

import com.txxzctest.springmvcdemo.annotation.NewController;
import com.txxzctest.springmvcdemo.annotation.NewRequestMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@NewController
@NewRequestMapping("/test")
public class TestController {
    @NewRequestMapping("/method")
    public void test(HttpServletRequest request, HttpServletResponse response) {
        try {
            response.getWriter().write("test success");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

访问该路由,测试成功
在这里插入图片描述

gitee 地址 :https://gitee.com/ChinaRage/easyspringmvcdemo

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值