java 手写一个简易版的SpringMVC

实现源码:https://gitee.com/lixiaoyue001/Controller-Annotation

 
主要思路:

        主要是手写了@Controller注解和@RequestMapping注解,然后在测试类上面添加注解,通过自己写好的mvc实现测试类里面的方法。

        实现MVC的思路:

  1. 首先获取类里面所有的java文件,将所有的文件名放入一个List中;
  2. 然后遍历这个list中的文件名,获取对应的类;
  3. 判断类中是否有@Controller注解和@RequestMapping注解;
  4. 如果有就用一个map存储类名以及类对象。
  5. 获取类中所有的方法,遍历所有的方法,若方法被@RequestMapping标注,则用另一个map存储映射路径以及method方法。
  6. 最后根据路径,利用反射实现被注解标注的方法。
实现步骤:
  1. 自定义注解:

        选择新建一个java Class,然后选择第四个注解即可生成一个注解类

        new-->java class -->Annotation  

     

 自定义Controller注解:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

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

       @Target表示注解(被它所注解的注解)可以使用到哪些地方,可以是类,方法,或者是属性上等。@Target(ElementType.TYPE)在这里表示Controller注解被使用到类上。

        @Retention是注解的有效时间,RetentionPolicy.RUNTIME是指程序运行的时候。

 

自定义RequestMapping注解:

import java.lang.annotation.*;

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

@Target({ElementType.METHOD, ElementType.TYPE})表示@RequestMapping注解可以加在类上和方法上;

2.扫描路径下面的文件:

该方法通过递归的方法将给定路径下面的文件都扫描出来,添加到一个List中返回。

//找到对应路径下面的文件
    private static List<String> scanFileWorker(String path){
        List<String> result=new ArrayList<>();

        File file=new File(path);
        File[] files=file.listFiles();
        for(File f:files){
            if(f.isFile()){
                result.add(f.getAbsolutePath());
            }else{
                result.addAll(scanFileWorker(f.getAbsolutePath()));
            }
        }
        return result;
    }

3. 将给定路径下面的文件里面的java文件过滤出来
//获取该路径下面所有的文件
        List<String> result=scanFileWorker(path);

        //将后缀为class的文件过滤出来
        result=result.stream().filter(item->{
            int index=item.lastIndexOf(".");
            String ext=item.substring(index+1);
            return Objects.equals("class", ext);
        }).collect(Collectors.toList());

4.遍历java文件名,获取每个java文件名对应的类,判断类里面是否有@Controller注解和@RequestMapping注解,如果有,获取@RequestMapping中的值和类对象的对应关系,存入一个map中;然后获取类里面所有的方法,若方法被@RequestMapping注解,则将url以及对应的方法实例存入另一个map中。
      for(String res:result){
                //从包名部分截取到后缀之前,以获取完整的包名+类名的形式
            String res1=res.substring(res.indexOf("qcby"),res.indexOf("."));
            String res2=res1.replace("\\",".");

            //获取对应的类
            Class<?> c=Class.forName(res2);
            //如果这个类里面有@Controller注解
            if(c.isAnnotationPresent(Controller.class)){
                System.out.println(c.getName()+"被注册成控制器");

                //如果这个类里面有@RequestM注解
                if(c.isAnnotationPresent(RequestMapping.class)){
                    String s=c.getAnnotation(RequestMapping.class).value(); //获取RequestMapping注解中的值

                    //如果map中已经存在key值了,抛出异常
                    if(map.containsKey(s)){
                        throw new RuntimeException("类多注解值:"+s);
                    }else{
                        //否则将@RequestMapping中的值作为key放入map中
                        map.put(s,new HashMap<>());
                        controllerMap.put(s,c.newInstance());
                    }

                    //获取类里面所有的方法
                    Method[] methods=c.getDeclaredMethods();
                    for(Method method:methods){
                        if(method.isAnnotationPresent(RequestMapping.class)){
                            //获取每个方法上面的RequestMapping注解中的值
                            String s1=method.getAnnotation(RequestMapping.class).value();
                            if(map.get(s).containsKey(s1)){
                                //抛出异常
                                throw new RuntimeException("方法多注解值:"+s1);
                            }else{
                                //将方法名上面的RequestMapping中的值以及对应的方法放入map中
                                map.get(s).put(s1,method);
                            }
                        }
                    }

                    System.out.println(map);
                    System.out.println(controllerMap);

                }else {
                    throw  new RuntimeException("类无requestMapping");
                }
            }
        }
5.传入类路径和方法路径,通过反射实现对应的方法:
//调用此方法实现测试类中被注解的方法
    public static void exec(String classpath,String methodPath){
        //如果controllerMap中没有这个类的名字
        if(controllerMap.get(classpath)==null){
            System.out.println("没有这个类 404");
        }else{
            //如果map中没有储存这个方法上面注解的值
            if(map.get(classpath).get(methodPath)==null){
                System.out.println("没有这个方法 404");
            }else{
                try {
                    //通过反射执行方法
                    map.get(classpath).get(methodPath).invoke(controllerMap.get(classpath));
                } catch (IllegalAccessException | InvocationTargetException e) {
                    throw new RuntimeException(e);
                }
            }
        }

    }
6.测试类如下:
import qcby.annotation.Controller;
import qcby.annotation.RequestMapping;

@Controller
@RequestMapping("test")
public class TestController {

    @RequestMapping()
    public String index(){
        System.out.println("test->index");
        return "";
    }

    @RequestMapping("index1")
    public String index1(){
        System.out.println("test->index1");
        return "";
    }
}

测试函数:

package qcby.Controller;


import qcby.mvc.Mvc;

import java.io.UnsupportedEncodingException;
import java.util.Objects;

public class Main {

    static {
        //获取自己的类所在的路径
        String path= Objects.requireNonNull(Main.class.getResource("")).getPath();
        try {
            //路径名中中文,因此用utf-8编码
            path=java.net.URLDecoder.decode(path,"utf-8").substring(1);
            //执行函数扫描路径下面的文件,并将带有注解的类和方法存入对应的map中
            Mvc.scanner(path);
        } catch (UnsupportedEncodingException | ClassNotFoundException | InstantiationException |
                 IllegalAccessException e) {
            throw new RuntimeException(e);
        }

    }

    public static void main(String[] args) {
        //执行对应的方法
        Mvc.exec("","");
        Mvc.exec("test","index1");
        Mvc.exec("test","");
        System.out.println("Hello World!");
    }
}

输出:

qcby.Controller.TestController被注册成控制器
{test={=public java.lang.String qcby.Controller.TestController.index(), index1=public java.lang.String qcby.Controller.TestController.index1()}}
{test=qcby.Controller.TestController@10f87f48}
没有这个类 404
test->index1
test->index
Hello World!

 第一个Mvc.exec("","")执行后,会在controllermap中寻找key为“”的键值对,因此并不能找到,输出了“没有这个类 404”

 第二个Mvc.exec("test","index1"),会在controllermap中找到key为“test”的键值对,在map中找到"index1"对应的方法,利用反射执行对应的方法;第三个会找到没有标注RequestMapping的方法并执行。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

每周都想吃火锅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值