之前一直对spring mvc 底层的实现原理狠挠头,直到网上听了一节课,花了一下午的时间跟着视频简单的实现了spring mvc。现整理下心得体会。

  个人对spring mvc的理解是 spring mvc = servlet + spring。其底层的实现技术主要是 自定义注解+反射。想要实现spring mvc 首先要先把spring mvc中常用的自定义注解整理实现出来。

1:常用注解

  (1):作用于Controller类上的@Controller 注解

/**
 * Created by zs.xu on 2020/8/13.
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XzsController {
    String value() default "";
}

(2):作用于Service实现类上的@Service注解

/**
 * Created by zs.xu on 2020/8/13.
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XzsService {
    String value() default "";
}

(3):作用于Controller类和其方法上用于映射路径的@RequestMapping注解

/**
 * Created by zs.xu on 2020/8/13.
 */
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XzsRequestMapping {
    String value() default "";
}

(4):作用于用于自动装配实现类的@Autowired 注解

/**
 * Created by zs.xu on 2020/8/13.
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XzsAutowired {
    String value() default "";
}

(5):作用于Controller方法上用于接收请求参数的@RequestParam注解

/**
 * Created by zs.xu on 2020/8/13.
 */
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XzsRequestParam {
    String value() default "";
}

2:底层核心方法都是在Servlet中实现的,所以项目在启动时,要根据servlet完成以下动作

  2.1:项目启动时servlet要初始化的一些数据

//用于保存所有的类的全路径名
    List<String> classList = new ArrayList<String>();
    //用于保存所有的bean实例 key:注解的值,value:对象                  的 实例)
    Map<String,Object> beanMap = new HashMap<String, Object>();
    //用于保存Controller里的url对呀的方法 key:类上加方法上的RequestMapping的值,value:对呀方法
    Map<String,Object> urlMap = new HashMap<String, Object>();
    public void init(ServletConfig config){
        //1:扫描出项目下所有的class文件,并存对应类的全路径名,以便于后面用反射实例化对象。
        scanPackage("com.xzs");
        //2: 根据第一步扫描出的class,配合类对应使用的注解,映射出类对应的实现类 保存到map(key:注解的值,value:对象                  的 实例)中。
        doInstance();
        //3:把service实例注入到Controller对象中的service属性
        doIoc();
        //4:把Controller的方法与@RequestMapping的value做映射关系,并存下来
        buildUrlMapping();
    }

     (1):扫描出项目下所有的class文件,并存对应类的全路径名,以便于后面用反射实例化对象。

          

//扫描"com.xzs"路径下文件
    private void scanPackage(String basePackage) {
        URL url = this.getClass().getClassLoader().getResource("/"+basePackage.replaceAll("\\.","/"));
        String fileStr = url.getFile();
        File file = new File(fileStr);
        String[] filesStr = file.list();
        for (String path : filesStr){
            //筛选出文件
            File filePath = new File(fileStr+path);
            if (filePath.isDirectory()){
                scanPackage( basePackage+"."+path);
            }else{
                //找到class类
                classList.add(basePackage+"."+filePath.getName());//内容是全类名路径
            }

        }
    }

      (2):根据第一步扫描出的class,配合类对应使用的注解,映射出类对应的实现类 保存到map(key:注解的值,value:对象                  的 实例)中。

//根据扫描出的list全类名,进行实例化
    private void doInstance() {
        if (null==classList ||classList.size()<=0){
            System.out.println("包扫描失败");
            return;
        }
        for (String className : classList){
            className = className.replace(".class","");

            try {
                Class<?> classZ = Class.forName(className);
                //判断有没有用注解
                if (classZ.isAnnotationPresent(XzsController.class)){
                    Object instance = classZ.newInstance();//创建控制类实例化对象
                    XzsRequestMapping requestMapping = classZ.getAnnotation(XzsRequestMapping.class);
                    String rmValue = requestMapping.value();
                    beanMap.put(rmValue,instance);
                }else if (classZ.isAnnotationPresent(XzsService.class)){
                    XzsService service = classZ.getAnnotation(XzsService.class);
                    Object instance = classZ.newInstance();
                    beanMap.put(service.value(),instance);
                }else{
                    continue;
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

     (3):把service实例注入到Controller对象中的service属性

//把service注入到controller控制层
    private void doIoc() {
        if (beanMap.entrySet().size() <= 0){
            System.out.println("没有一个被实例化的类");
        }

        for (Map.Entry<String, Object> entry: beanMap.entrySet()) {
            Object instance = entry.getValue();
            Class<?> clasz = instance.getClass();
            if (clasz.isAnnotationPresent(XzsController.class)){
                //@XzsAutowired("LYTService")
                //private LYTService lytService;
                Field[] fields = clasz.getDeclaredFields();
                for (Field field:fields){
                    //判断属性值上有没有注解
                    if (field.isAnnotationPresent(XzsAutowired.class)){
                        XzsAutowired annotation = field.getAnnotation(XzsAutowired.class);
                        String key = annotation.value();//"LYTService"
                        field.setAccessible(true);//private类型 的属性  注入不进去,需要放开权限
                        //设置值
                        try {
                            field.set(instance,beanMap.get(key));
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }else{
                        continue;
                    }
                }
            }
        }
    }

      (4):把Controller的方法与@RequestMapping的value做映射关系,并存下来

//4:url 映射 把方法和请求地址做关联 ;;;/xzs/select 找到对应的方法
    private void buildUrlMapping() {
        if (beanMap.entrySet().size() <= 0){
            System.out.println("没有一个被实例化的类");
        }
        for (Map.Entry<String, Object> entry : beanMap.entrySet()) {
            Class<?> clasz = entry.getValue().getClass();
            if (clasz.isAnnotationPresent(XzsController.class)){
                XzsRequestMapping controllerMapping = clasz.getAnnotation(XzsRequestMapping.class);
                String controllerUrl = controllerMapping.value();//拿到了---》/xzs
                Method[] methods = clasz.getMethods();
                for (Method method:methods){
                    //判断属性值上有没有注解
                    if (method.isAnnotationPresent(XzsRequestMapping.class)){
                        XzsRequestMapping methodMapping = method.getAnnotation(XzsRequestMapping.class);
                        String methodUrl = methodMapping.value();//拿到了---》/select
                        //controllerUrl+methodUrl == /xzs/select
                        //key:url路径 ,value:待执行的方法
                        urlMap.put(controllerUrl+methodUrl,method);
                    }else{
                        continue;
                    }
                }
            }
        }
    }

2.1:Servlet重新继承的HttpServlet的 doGet(),doPost()方法,经过处理,拿到请求路径 ,找到请求路径对应的Controller方法,解析出对应的请求参数然后执行对应的方法

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

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //根据路径拿到对应的方法
        //1:获取请求路径--
        String requestURI = req.getRequestURI();
        String contextPath = req.getContextPath();
        String path = requestURI.replace(contextPath, "");//urlMap的key

        //2:拿到方法
        Method method = (Method)urlMap.get(path);
        String[] split = path.split("/");
        String controllerPath=null!=split&&split.length>1?"/"+split[1]:"";
        //3:拿到方法对应的控制类
        Object o = beanMap.get(controllerPath);
        //4:参数处理
        Object arg[] = hand(req,resp,method);
        try {
            method.invoke(o,arg);
        } catch (IllegalAccessException e) {


        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
    //获取方法中的参数
    private static Object[] hand(HttpServletRequest request, HttpServletResponse response,Method method){
        //拿到当前执行方法有哪些参数
        Class<?>[] paramClazzs = method.getParameterTypes();
        //根据参数个数,new一个参数的数组,将方法里的所有参数赋值大agrs中
        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++] = request;
            }
            if(ServletResponse.class.isAssignableFrom(paramClazz)){
                args[args_i++] = response;
            }

            Annotation[] paramAns = method.getParameterAnnotations()[index];
            if(paramAns.length>0){
                for (Annotation paramAn : paramAns) {
                    if(XzsRequestParam.class.isAssignableFrom(paramAn.getClass())){
                        XzsRequestParam mp = (XzsRequestParam) paramAn;
                        //找到注解里的name和value
                        args[args_i++] = request.getParameter(mp.value());
                    }
                }
            }
            index++;
        }
        return args;
    }