likes
comments
collection
share

唰唰的手撕一个简单的Spring Mvc 框架

作者站长头像
站长
· 阅读数 20

文章目录

自定义实现Spring Mvc框架

前言

在使用Spring Mvc时候我们需要配置一个中央处理器DispatcherServlet用于分发请求,根据url查找处理器,然后再根据处理器去处理不同的Url去执行不同的方法,最后到处理完数据,再返回给前端;这一过程都是在DispatcherServlet处理器中去执行的;

可以看到下图是SpringMvc的请求流程图 唰唰的手撕一个简单的Spring Mvc 框架

当url进来的时候我们就应该去加载完成url和一些方法的对应关系,以及注册和处理好bean的依赖关系;

如需要自己去实现一个这个框架:

可以思考一下:

1 需要提供一个路径去扫描包下的所有类 2 然后解析处理类,注册bean 维护依赖关系 3 处理好url和方法的对应关系 4 在DispatcherServlet中取根据前端请求的url去查找到具体的处理器,然后再根据url去找到具的执行方法,去执行反方,返回给前端;

具体操作:

步骤

  • 1 启动tomcat DisPatcherServlet 加载配置文件SpringMvc.xml

  • 2 指定包扫描,扫描注解

    @Controllelr @Service @Autowired @RequestMapping 维护url映射

  • 3 加入IOC容器 初始化对象,维护依赖关系,维护url映射

  • 4 SpringMVC相关组件初始化

    简历Url和method之间的映射关系—> 映射器 HanderMappping

  • 5 请求处理

代码实现

环境搭建

JDK8 ,IDEA 2019 03

依赖

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


        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.9</version>
        </dependency>

插件


        <plugins>

            <!-- 配置Maven的JDK编译级别 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.2</version>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                    <encoding>UTF-8</encoding>
                    <!--告诉编译器,编译的时候记录下形参的真实名称-->
                    <compilerArgs>
                        <arg>-parameters</arg>
                    </compilerArgs>
                </configuration>
            </plugin>

            <!-- tomcat7插件 -->
            <!-- 注意:目前来说,maven中央仓库还没有tomcat8的插件 -->
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <port>8080</port>
                    <path>/</path>
                </configuration>
            </plugin>

        </plugins>

自定义注解

Autowired

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
    String name() default "";

}

Controller

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Controller {
}

RequestMapping

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestMapping {
    String value() default "";
}

Service

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Service {
    String value() default "";
}

定义service 和 controller代码

Test控制层

@RequestMapping("/api")
@Controller
public class Test {

    @Autowired
    private TestService testService;

    @RequestMapping("/test")
    public String test(HttpServletRequest request, HttpServletResponse response, String name) {

        return testService.test(name);
    }
}

TestService 接口

public interface TestService {

    String test(String name);
}

TestServiceImpl实现类

@Service(value = "testServiceImpl")
public class TestServiceImpl implements TestService {

    @Override
    public String test(String name) {
        System.out.println("-------------------- name = " + name);
        return "--------- " + name;
    }

}

定义DisPatcherServlet处理器

处理前端所有的请求都走DisPatcherServlet处理器

    <!--    处理请求 -->
    <servlet-mapping>
        <servlet-name>mvc-servlte</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
    

DisPatcherServlet 继承 HttpServlet 接口,重写init get post方法

public class DisPatcherServlet extends HttpServlet {
    @Override
    public void init() throws ServletException {
    // 初始化参数 加载配置文件,bean实例化 以及依赖维护
		    //1 加载配置文件
            doLoadConfigFile(resourceAsStream);
            // 2 扫描包
            doScanPackage();
            // 3  bean实例化和依赖维护
            doInitBean();
            // 4 维护url和method之间的映射关系
            doHandlerMapping();
            
}
   @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

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

 // 处理请求 
}
}

初始化反方里 需要我们去加载bean 实例化和依赖维护,以及封装好url对应的方法handler

定义方法

    //1 加载配置文件
            doLoadConfigFile(resourceAsStream);
            // 2 扫描包
            doScanPackage();
            // 3  bean实例化和依赖维护
            doInitBean();
            // 4 维护url和method之间的映射关系
            doHandlerMapping();

加载配置文件

对应到springmvc中就是包扫描配置扫描那些包 这儿配置一个properties文件 mvc.properties配置需要扫描的包路径

mvc.path=com.udem.edu

通过类加载器去加载配置文件获取到流

  //加载流
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("mvc.properties");

然后获取包路径封装文件到 Properties中

    /**
     * 加载配置文件
     *
     * @param resourceAsStream
     */
    private void doLoadConfigFile(InputStream resourceAsStream) {
        try {
            properties.load(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

包扫描

包路径转换成文件路径然后去扫描,判断是否文件夹或文件,然后保存类的全限定命名到集合中,这个没得说;


    /**
     * 包扫描获取字节码对象
     *
     * @throws UnsupportedEncodingException
     */
    private void doScanPackage() throws UnsupportedEncodingException {
        //获取mvc包路径
        String packName = properties.getProperty("mvc.path");
        if (Objects.isNull(packName) || packName.length() == 0) {
            throw new RuntimeException("无效的包路径");
        }
        packName = packName.replace(".", File.separator);
        URL resource = this.getClass().getClassLoader().getResource(packName);
        String path = resource.getPath();
        //解析中文
        String filePath = URLDecoder.decode(path, "UTF-8");

        //解析包成java权限定命名com
        parseFilePackName(packName, stringSet, filePath);

    }

bean实例化和依赖维护

判断类上的注解以及封装到IOC 依赖维护赋值值给含有 Autowired 注解的成员变量

  /**
     * bean实例化和bean依赖注入关系维护
     *
     * @throws ClassNotFoundException
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    private void doInitBean() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        for (String className : stringSet) {
            //反射创建对象
            Class<?> aClass = Class.forName(className);
            //查找 Controller 注解
            Controller annotation = aClass.getAnnotation(Controller.class);
            if (Objects.nonNull(annotation)) {
                //实例化存入ioc
                IOC_MAP.put(aClass.getSimpleName(), aClass.newInstance());
            }
            //查找 service 注解
            Service service = aClass.getAnnotation(Service.class);
            if (Objects.nonNull(service)) {
                //实例化存入ioc
                IOC_MAP.put(aClass.getSimpleName(), aClass.newInstance());
                //如果实现类接口 按照接口实例化
                Class<?>[] interfaces = aClass.getInterfaces();
                if (Objects.nonNull(interfaces) && interfaces.length > 0) {
                    for (Class<?> anInterface : interfaces) {
                        //按照接口名进行实例化
                        IOC_MAP.put(anInterface.getSimpleName(), aClass.newInstance());
                    }

                }
            }
        }


        //依赖注入
        for (Map.Entry<String, Object> stringObjectEntry : IOC_MAP.entrySet()) {

            Class aClass = stringObjectEntry.getValue().getClass();
            //获取所有字段
            Field[] declaredFields = aClass.getDeclaredFields();

            if (Objects.isNull(declaredFields) && declaredFields.length == 0) {
                continue;
            }

            for (Field field : declaredFields) {
                //field.get
                //字段含有 Autowired 注解的需要被自动装配对象
                Autowired autowired = field.getAnnotation(Autowired.class);

                if (Objects.nonNull(autowired)) {
                    //根据当前key获取需要注入示例对象
                    //先根据名字注入,如果名字获取不到,再根据类型去注入
                    String beanName = autowired.name();

                    if (Objects.isNull(beanName) || beanName.length() == 0) {
                        beanName = field.getType().getSimpleName();
                    }

                    //反射设置值
                    try {
                        field.setAccessible(true);
                        //自动装配 线程不安全,Spring中默认单例
                        field.set(stringObjectEntry.getValue(), IOC_MAP.get(beanName));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

    }

handlerMapper 处理映射器

维护url和method之间的映射关系 这儿需要处理控制层的参数方法,也就是控制层方法的参数顺序,需要和之后 DisPatcherServlet中请求处理传递进来的参数保持一致

在 DisPatcherServlet 中根据请求url获取到这里封装的 handle 然后传递请求的参数与原方法需要的参数保持一致,反射调用;

封装 handle 对象用于保存参数位置,以及那个方法,那个控制器!

/**
 * 封装handler方法相关的信息
 * <p>
 * handler参数相关信息
 */
public class Handle {


    /**
     * method.invoke(obj 需要执行得对象
     */
    private Object controller;

    /**
     * url正则匹配对象
     */
    private Pattern pattern;

    /**
     * 需要调用得url对应得方法
     */
    private Method method;

    /**
     * 存储参数顺序,为了进行参数绑定  Integer 第几个参数
     */
    private Map<String, Integer> map;


    public Handle(Object controller, Pattern pattern, Method method) {
        this.controller = controller;
        this.pattern = pattern;
        this.method = method;
        map = new HashMap<>();
    }

   // get  set省略
}

封装handler方法相关的信息

这儿我们目前只处理String类型的传参,其他对象之类的暂时不涉及到…

    private void doHandlerMapping() {

        if (IOC_MAP.isEmpty()) {
            return;
        }

        //只处理controller层
        for (Map.Entry<String, Object> stringObjectEntry : IOC_MAP.entrySet()) {

            Class<?> objectEntryValue = stringObjectEntry.getValue().getClass();
            //没有包含 注解 跳过
            if (!objectEntryValue.isAnnotationPresent(Controller.class)) {
                continue;
            }

            String url = "";
            //包含 RequestMapping 注解 就获取url
            if (objectEntryValue.isAnnotationPresent(RequestMapping.class)) {
                RequestMapping requestMapping = objectEntryValue.getAnnotation(RequestMapping.class);
                url = requestMapping.value();
            }


            //获取方法
            Method[] methods = objectEntryValue.getMethods();
            if (methods.length <= 0 || Objects.isNull(methods)) {
                continue;
            }

            for (Method method : methods) {
                //如果不包含就跳过
                if (!method.isAnnotationPresent(RequestMapping.class)) {
                    continue;
                }
                //获取当前方法上 RequestMapping 的注解
                RequestMapping requestMapping = method.getDeclaredAnnotation(RequestMapping.class);
                //构建url
                String urls = url + requestMapping.value();

                //封装handler
                Handle handle = new Handle(stringObjectEntry.getValue(), Pattern.compile(urls), method);

                //处理计算参数位置
                Parameter[] parameters = method.getParameters();
                if (parameters != null && parameters.length > 0) {
                    for (int i = 0; i < parameters.length; i++) {
                        Parameter parameter = parameters[i];
                        if (parameter.getType() == HttpServletRequest.class || parameter.getType() == HttpServletResponse.class) {
                            //如果是 参数名称存本身
                            handle.getMap().put(parameter.getType().getSimpleName(), i);
                        }
                        //如果是tring类型
                        else {
                            //String类型存储name
                            handle.getMap().put(parameter.getName(), i);
                        }
                        //其他类型暂时不做判断
                    }
                    HANDLE_MAPPING.add(handle);
                }


            }

        }


    }

doPost中 处理请求

根据HttpServletRequest 获取到请求参数以及url

 
        String requestURI = req.getRequestURI();
        //遍历请求中得参数顺序
        Map<String, String[]> reqParameterMaps = req.getParameterMap();

        System.out.println("------- 当前请求的url是 : " +  requestURI);
        if (Objects.isNull(requestURI) || requestURI.length() == 0) {
            return;
        }

获取这个请求url的处理器handler ,如果不存在返回404

  //5.1 获取handle
        Handle handler = getHandler(req);
   if (Objects.isNull(handler)){
            resp.getWriter().println("404....");
            return;
        }

获取到具体的执行方法以及方法中的参数顺序 也就是封装handler时候封装的map

 //获取执行方法
        Method method = handler.getMethod();

        Parameter[] parameters = handler.getMethod().getParameters();
        //5.2 获取型参数
        Map<String, Integer> map1 = handler.getMap();

设置请求的url中的请求参数 与 method中的方法参数一致

  //设置传递参数 
        Object[] objects = new Object[parameters.length];


      //遍历除reqest 和 response之外的参数 并且填充到参数数组里
        for (Map.Entry<String, String[]> stringEntry : reqParameterMaps.entrySet()) {
            //判断请求参数和形参是否参数匹配
            //多个参数直接 , 拼接
            String value = StringUtils.join(stringEntry.getValue(), ",");  // 如同 1,2
            if (!map1.containsKey(stringEntry.getKey())) {
               continue;
            }
            //获取型参数索引
            Integer index = map1.get(stringEntry.getKey());

            //赋值参数
            objects[index] = value;
        }

  int index1 = map1.get(HttpServletRequest.class.getSimpleName());
        int index2 = map1.get(HttpServletResponse.class.getSimpleName());
        //获取ret对象参数
        objects[index1] = req;
        //获取resp对象参数
        objects[index2] = resp;

反射执行方法


        try {
           String result = (String) method.invoke(handler.getController(), objects);
           resp.getWriter().println(result);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

完整的DisPatcherServlet代码

package com.udem.mvcframework.servlet;

import com.udem.mvcframework.annotation.Autowired;
import com.udem.mvcframework.annotation.Controller;
import com.udem.mvcframework.annotation.RequestMapping;
import com.udem.mvcframework.annotation.Service;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.net.URL;
import java.net.URLDecoder;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * 前端请求中央处理器
 */
public class DisPatcherServlet extends HttpServlet {

    /**
     * 封装配置文件属性
     */
    private final static Properties properties = new Properties();

    /**
     * 存放扫描类的全限定命名
     */
    private final static Set<String> stringSet = new HashSet<>();

    /**
     * .class
     */
    public static final String CLASS_STR = ".class";


    /**
     * 存储bean单例
     */
    public final static Map<String, Object> IOC_MAP = new HashMap<>();


    /**
     * 构建映射信息
     */
    public final static List<Handle> HANDLE_MAPPING = new ArrayList<>();


    @Override
    public void init() throws ServletException {
        //加载流
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("mvc.properties");

        System.out.println("----------------- 初始化 -------------------");
        super.init();


        try {
            //1 加载配置文件
            doLoadConfigFile(resourceAsStream);
            // 2 扫描包
            doScanPackage();
            // 3  bean实例化和依赖维护
            doInitBean();
            // 4 维护url和method之间的映射关系
            doHandlerMapping();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        System.out.println("------------  ioc容器bean \n " + IOC_MAP);

        System.out.println("----------------- 初始化完成 -------------------");
    }


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

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

        //5 判断请求Url和方法url 执行相应方法
        String requestURI = req.getRequestURI();
        //遍历请求中得参数顺序
        Map<String, String[]> reqParameterMaps = req.getParameterMap();

        System.out.println("------- 当前请求的url是 : " +  requestURI);
        if (Objects.isNull(requestURI) || requestURI.length() == 0) {
            return;
        }
        //5.1 获取handle
        Handle handler = getHandler(req);

        if (Objects.isNull(handler)){
            resp.getWriter().println("404....");
            return;
        }
        //获取执行方法
        Method method = handler.getMethod();

        Parameter[] parameters = handler.getMethod().getParameters();
        //5.2 获取型参数
        Map<String, Integer> map1 = handler.getMap();
        //5.3 获取参数长度
        //设置传递参数
        Object[] objects = new Object[parameters.length];

        //保证参数顺序和方法中的型参数顺序一致

        //遍历除reqest 和 response之外的参数 并且填充到参数数组里
        for (Map.Entry<String, String[]> stringEntry : reqParameterMaps.entrySet()) {
            //判断请求参数和形参是否参数匹配
            //多个参数直接 , 拼接
            String value = StringUtils.join(stringEntry.getValue(), ",");  // 如同 1,2
            if (!map1.containsKey(stringEntry.getKey())) {
               continue;
            }
            //获取型参数索引
            Integer index = map1.get(stringEntry.getKey());

            //赋值参数
            objects[index] = value;
        }

        int index1 = map1.get(HttpServletRequest.class.getSimpleName());
        int index2 = map1.get(HttpServletResponse.class.getSimpleName());
        //获取ret对象参数
        objects[index1] = req;
        //获取resp对象参数
        objects[index2] = resp;

        try {
           String result = (String) method.invoke(handler.getController(), objects);
           resp.getWriter().println(result);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

    }

    private Handle getHandler(HttpServletRequest req) {

        // 根据url找到对应得method方法 并且执行调用
        for (Handle handle : HANDLE_MAPPING) {


            Matcher matcher = handle.getPattern().matcher(req.getRequestURI());
            if (matcher.find()) {
                return handle;
            }


        }

        return null;
    }

    /**
     * 加载配置文件
     *
     * @param resourceAsStream
     */
    private void doLoadConfigFile(InputStream resourceAsStream) {
        try {
            properties.load(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * handlerMapper 处理映射器
     * 建立url和方法一致
     */
    private void doHandlerMapping() {

        if (IOC_MAP.isEmpty()) {
            return;
        }

        //只处理controller层
        for (Map.Entry<String, Object> stringObjectEntry : IOC_MAP.entrySet()) {

            Class<?> objectEntryValue = stringObjectEntry.getValue().getClass();
            //没有包含 注解 跳过
            if (!objectEntryValue.isAnnotationPresent(Controller.class)) {
                continue;
            }

            String url = "";
            //包含 RequestMapping 注解 就获取url
            if (objectEntryValue.isAnnotationPresent(RequestMapping.class)) {
                RequestMapping requestMapping = objectEntryValue.getAnnotation(RequestMapping.class);
                url = requestMapping.value();
            }


            //获取方法
            Method[] methods = objectEntryValue.getMethods();
            if (methods.length <= 0 || Objects.isNull(methods)) {
                continue;
            }

            for (Method method : methods) {
                //如果不包含就跳过
                if (!method.isAnnotationPresent(RequestMapping.class)) {
                    continue;
                }
                //获取当前方法上 RequestMapping 的注解
                RequestMapping requestMapping = method.getDeclaredAnnotation(RequestMapping.class);
                //构建url
                String urls = url + requestMapping.value();

                //封装handler
                Handle handle = new Handle(stringObjectEntry.getValue(), Pattern.compile(urls), method);

                //处理计算参数位置
                Parameter[] parameters = method.getParameters();
                if (parameters != null && parameters.length > 0) {
                    for (int i = 0; i < parameters.length; i++) {
                        Parameter parameter = parameters[i];
                        if (parameter.getType() == HttpServletRequest.class || parameter.getType() == HttpServletResponse.class) {
                            //如果是 参数名称存本身
                            handle.getMap().put(parameter.getType().getSimpleName(), i);
                        }
                        //如果是tring类型
                        else {
                            //String类型存储name
                            handle.getMap().put(parameter.getName(), i);
                        }
                        //其他类型暂时不做判断
                    }
                    HANDLE_MAPPING.add(handle);
                }


            }

        }


    }


    /**
     * bean实例化和bean依赖注入关系维护
     *
     * @throws ClassNotFoundException
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    private void doInitBean() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        for (String className : stringSet) {
            //反射创建对象
            Class<?> aClass = Class.forName(className);
            //查找 Controller 注解
            Controller annotation = aClass.getAnnotation(Controller.class);
            if (Objects.nonNull(annotation)) {
                //实例化存入ioc
                IOC_MAP.put(aClass.getSimpleName(), aClass.newInstance());
            }
            //查找 service 注解
            Service service = aClass.getAnnotation(Service.class);
            if (Objects.nonNull(service)) {
                //实例化存入ioc
                IOC_MAP.put(aClass.getSimpleName(), aClass.newInstance());
                //如果实现类接口 按照接口实例化
                Class<?>[] interfaces = aClass.getInterfaces();
                if (Objects.nonNull(interfaces) && interfaces.length > 0) {
                    for (Class<?> anInterface : interfaces) {
                        //按照接口名进行实例化
                        IOC_MAP.put(anInterface.getSimpleName(), aClass.newInstance());
                    }

                }
            }
        }


        //依赖注入
        for (Map.Entry<String, Object> stringObjectEntry : IOC_MAP.entrySet()) {

            Class aClass = stringObjectEntry.getValue().getClass();
            //获取所有字段
            Field[] declaredFields = aClass.getDeclaredFields();

            if (Objects.isNull(declaredFields) && declaredFields.length == 0) {
                continue;
            }

            for (Field field : declaredFields) {
                //field.get
                //字段含有 Autowired 注解的需要被自动装配对象
                Autowired autowired = field.getAnnotation(Autowired.class);

                if (Objects.nonNull(autowired)) {
                    //根据当前key获取需要注入示例对象
                    //先根据名字注入,如果名字获取不到,再根据类型去注入
                    String beanName = autowired.name();

                    if (Objects.isNull(beanName) || beanName.length() == 0) {
                        beanName = field.getType().getSimpleName();
                    }

                    //反射设置值
                    try {
                        field.setAccessible(true);
                        //自动装配 线程不安全,Spring中默认单例
                        field.set(stringObjectEntry.getValue(), IOC_MAP.get(beanName));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

    }


    /**
     * 包扫描获取字节码对象
     *
     * @throws UnsupportedEncodingException
     */
    private void doScanPackage() throws UnsupportedEncodingException {
        //获取mvc包路径
        String packName = properties.getProperty("mvc.path");
        if (Objects.isNull(packName) || packName.length() == 0) {
            throw new RuntimeException("无效的包路径");
        }
        packName = packName.replace(".", File.separator);
        URL resource = this.getClass().getClassLoader().getResource(packName);
        String path = resource.getPath();
        //解析中文
        String filePath = URLDecoder.decode(path, "UTF-8");

        //解析包成java权限定命名com
        parseFilePackName(packName, stringSet, filePath);

    }


    /**
     * 递归处理路径下文件夹是否包含文件夹,如不包含则获取当前类的权限定命名存入set中
     *
     * @param packName
     * @param classNameSet
     * @param path
     */
    public static void parseFilePackName(String packName, Set<String> classNameSet, String path) {

        File packNamePath = new File(path);

        if (!packNamePath.isDirectory() || !packNamePath.exists()) {
            return;
        }
        //递归路径下所有文件和文件夹
        for (File file : packNamePath.listFiles()) {
            boolean directory = file.isDirectory();
            String classNamePath = packName + File.separator + file.getName().replace(File.separator, ".");
            if (directory) {
                parseFilePackName(classNamePath, classNameSet, file.getPath());
            } else if (file.isFile() && file.getName().endsWith(CLASS_STR)) {
                //存入set
                classNameSet.add(classNamePath.replace(File.separator, ".").replace(CLASS_STR, ""));
            }
        }

    }
}

测试

启动tomcat

首先输入http://localhost:8080/kkkkk 可以看到首次请求容器初始化完成 各种bean实例化以及依赖维护.handler封装完成

请求的这个地址不存在,返回 404…

唰唰的手撕一个简单的Spring Mvc 框架

然后我们再输入自己的地址 http://localhost:8080/api/test?name=123&name=000 可以看到 唰唰的手撕一个简单的Spring Mvc 框架

代码地址

gir地址