只要肯花时间,一气呵成手写SpringMVC框架不是问题!

本文详细介绍了手写一个简化版SpringMVC框架的过程,包括自定义注解、DispatcherServlet实现、反射机制下的bean实例化及依赖注入,以及前端请求与后端method的映射。通过具体代码示例,展示了如何构建一个轻量级的MVC框架。
摘要由CSDN通过智能技术生成

一、Git项目地址:

 

地址:https://github.com/kobeyk/handwriting-springmvc.git

 

 

网上可以搜到很多,大致思路和手写步骤如下:

1、仿Spring注解,自定义一套属于自己的注解,如仿写常用的@Controller、@Service、@AutoWired等

2、自定义DispatcherServlet类,继承HttpServlet类,重写几个方法,如servlet的初始化方法init()

 

 

3、在init方法中,做一些Servlet容器初始化的工作:

   3.1 扫描package下面的所有class,将class的完全限定名保存在List集合中

   3.2 实例化第一步集合中的Bean对应的实例,实例的过程采用的是Java的反射机制,且哪些bean是需要实例化的是有过滤条件 的,如加了@Controller注解的bean类才可以newInstance(),实例化的结果,放在beanMap中进行存储,map中的key就是    bean的name,value就是bean实例。

   3.3 实例化单个bean后,接着要处理单个bean中依赖注入的问题,也就是要处理@AutoWired注解的类字段了

   3.4 bean依赖注入的问题解决后,剩下的就是解决前端请求url和后端method之间的映射(Mapping)了,也就是说,前端发送url请求后,后端,也就是Servlet容器是如何知道要去找哪个Controller中的哪个对应的method去执行呢?这就需要我们再次通过反射技术,去找寻url和method之间的关系了,找到后,存放在mapping容器中,以便于doGet和doPost方法中取出调用。

4、最后,配置项目的web.xml

 

 


 

二、项目结构

 

 


 

三、核心代码(部分)

 

public class MyDispatcherServlet extends HttpServlet {

    // 重写三个方法,一个是父类GenericServlet的初始化方法init(),两个分别是父类HttpServlet的doGet和doPost请求处理方法

    /**存放指定的扫描包下面的所有的class*/
    List<String> classNames = new ArrayList<>();

    /**存放符合条件的实例化后的bean(类似于简版的ioc容器),如加了xxx注解的bean要放进来*/
    Map<String,Object> beanMap = new HashMap<>(16);

    /**url和method映射关系 (请求api地址和处理请求的方法的键值对)*/
    Map<String, Method> urlMethodMap = new HashMap<>(16);

    @Override
    public void init() throws ServletException {
        // 1、第一步,扫描指定的包(主要是扫描xxx.class文件)
        scanPackage("com.appleyk");
        System.out.println("classNames = " + classNames);
        // 2、利用反射,实例化bean(将实例化的bean,放入bean容器map中)
        beanInstance();
        System.out.println("beanMap = " + beanMap);
        //3、处理@XXAutoWired注解,bean实例中的字段如果有这个注解(依赖其他bean),则将该注解对应的实例从beanMap中取出,并赋予该字段
        doAutoWired();
        // 4、处理请求的url与控制器类中方法的mapping(映射)关系
        handleMapping();
        System.out.println("urlMethodMap = " + urlMethodMap);
    }



    /**
     * 获取指定扫描包下面的所有class的名称(限定类名)
     * @param packageStr 要扫描的包名
     */
    public void scanPackage(String packageStr){

        // 1、将xx.xx结构的包名,转换为实际意义上的xx/xx/路径
        String pageckageDir = packageStr.replaceAll("\\.", "/");
        // 2、根据包路径,拿到当前类路径(classPath)
        URL resource = this.getClass().getClassLoader().getResource(pageckageDir);
        // 3、拿到资源的文件(全)路径
        String fileStr = resource.getFile();
        // 4、根据路径创建文件
        File file = new File(fileStr);
        // 5、判断文件是否存在,不存在直接返回
        if(!file.exists()){
            return;
        }

        // 6、获取classes文件下面的所有文件集合(可能是文件,也有可能是目录)
        File[] files = file.listFiles();
        for (File filePath : files) {
            String className = packageStr + "." + filePath.getName().replace(".class","");
            if(filePath.isDirectory()){
                // 如果是文件夹的话,递归继续获取类限定名
                scanPackage(className);
            }else{
                // 如果是文件的话,直接包名+“.”+文件拼接成类限定名,然后加入到集合中
                classNames.add(className);
            }
        }

    }


    /**
     * 首字母小写
     */
    private String lowerFirstCase(String str){
        char[] chars = str.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }

}

 


 

 

四、如何更改MVC项目的contextPath

 

 


 

 

五、启动Tomcat,来一波测试,验证手写300多行代码的实际效果

 

(1)run

 

 


 

(2)浏览器输入请求url

 

 


 

https://localhost:8080/springmvc/user/query

 

A: 默认不给参数的调用情况

 


 

 

 


 

B: 带参数的调用情况

 

https://localhost:8080/springmvc/user/query?name=appleyk&sex=%E7%94%B7

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值