springmvc原理

准备工作

1、新建gitee代码库

gitee地址https://gitee.com/study.liu/Lspring/tree/develop/

仓库地址https://gitee.com/study.liu/Lspring.git

2、IDE工具通过git克隆到本地,切换develop分支

image

image

项目构建过程不详述、包结构如下:

images

springmvc原理

流程图

images

实现要点

1、包扫描

2、类的实例化

3、依赖注入|完成装配

4、url和Controller中的method的关系映射

Talk is cheap. Show me the code. ---- Linus Torvalds

代码

@Autowired

package ai.ls.springmvc.annotation;

import java.lang.annotation.*;

/**
 * @BelongsProject: Lspring
 * @BelongsPackage: ai.ls.springmvc.annotation
 * @Author: ls
 * @CreateTime: 2020-12-29 15:18
 * @Description: 模拟@Autowired
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Autowired {

    String value() default "";

}


@Controller

package ai.ls.springmvc.annotation;

import java.lang.annotation.*;

/**
 * @BelongsProject: Lspring
 * @BelongsPackage: ai.ls.annotation
 * @Author: ls
 * @CreateTime: 2020-12-29 15:15
 * @Description: 模拟@Controller
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Controller {
    String value() default "";
}

@RequestMapping

package ai.ls.springmvc.annotation;

import java.lang.annotation.*;

/**
 * @BelongsProject: Lspring
 * @BelongsPackage: ai.ls.springmvc.annotation
 * @Author: ls
 * @CreateTime: 2020-12-29 15:17
 * @Description: 模拟@RequestMapping
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface RequestMapping {
    String value() default "";
}

@Service

package ai.ls.springmvc.annotation;

import java.lang.annotation.*;

/**
 * @BelongsProject: Lspring
 * @BelongsPackage: ai.ls.springmvc.annotation
 * @Author: ls
 * @CreateTime: 2020-12-29 15:19
 * @Description: 模拟@Service
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Service {

    String value() default "";
}

DispatcherServlet


package ai.ls.springmvc.servlet;

import ai.ls.springmvc.annotation.Autowired;
import ai.ls.springmvc.annotation.Controller;
import ai.ls.springmvc.annotation.RequestMapping;
import ai.ls.springmvc.annotation.Service;

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.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @BelongsProject: Lspring
 * @BelongsPackage: ai.ls.springmvc.servlet
 * @Author: ls
 * @CreateTime: 2020-12-29 16:56
 * @Description: 模拟DispatcherServlet
 */
public class DispatcherServlet extends HttpServlet {

    List<String> clazzNames = new ArrayList<>();

    Map<String, Object> beans = new HashMap<>();

    Map<String, Object> handlerMethod = new HashMap<>();

    public DispatcherServlet() {
    }

    @Override
    public void init() throws ServletException {
        //1、包扫描
        scanPackage("ai.ls");
        //2、实例化类
        classInstance();
        //3.依赖注入/自动装配
        inject();
        //4、url和Controller中method的关系映射
        handlerMapping();


    }
    private void  handlerMapping() {
        if (beans.isEmpty()) {
            System.out.println("没有实例化的类");
            return;
        }
        for (Map.Entry<String, Object> entry : beans.entrySet()) {
            //从集合中获取类实例
            Object instance = entry.getValue();
            if (instance.getClass().isAnnotationPresent(Controller.class)) {
                RequestMapping requestMapping = instance.getClass().getAnnotation(RequestMapping.class);
                //获取RequestMapping中定义的路径值
                String path = requestMapping.value();
                Method[] methods = instance.getClass().getMethods();
                for (Method method : methods) {
                    if (method.isAnnotationPresent(RequestMapping.class)) {
                        RequestMapping methodMapping = method.getAnnotation(RequestMapping.class);
                        String value = methodMapping.value();
                        //类上的路径+方法上路径
                        handlerMethod.put(path + value, method);
                    }
                }

            }

        }
    }
    private void inject() {
        if (beans.isEmpty()) {
            System.out.println("没有实例化的类");
            return;
        }
        for (Map.Entry<String, Object> entry : beans.entrySet()) {
            //从集合中获取类实例
            Object instance = entry.getValue();
            //获取类中的所有成员属性
            Field[] fields = instance.getClass().getDeclaredFields();
            for (Field field : fields) {
                //判断成员属性上有无Autowired注解
                if (field.isAnnotationPresent(Autowired.class)) {
                    Autowired autowired = field.getAnnotation(Autowired.class);
                    String value = autowired.value();

                    field.setAccessible(true);

                    try {
                        //完成依赖注入
                        field.set(instance,beans.get(value));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }

            }
        }

    }
    private void classInstance() {
        if (clazzNames.isEmpty()) {
            System.out.println("没有扫描到类");
            return;
        }
        for (String name : clazzNames) {
            String realName = name.replace(".class", "");
            try {
                Class<?> aClass = Class.forName(realName);

                if (aClass.isAnnotationPresent(Controller.class)) {
                    Controller controller = aClass.getAnnotation(Controller.class);
                    //完成Controller的实例化
                    Object instance = aClass.newInstance();

                    RequestMapping requestMapping = aClass.getAnnotation(RequestMapping.class);
                    String mappingValue = requestMapping.value();

                    //类的映射路径和类的实例化对象进行绑定
                    beans.put(mappingValue, instance);
                }

                if (aClass.isAnnotationPresent(Service.class)) {
                    Service service = aClass.getAnnotation(Service.class);
                    //完成Service的实例化
                    Object instance = aClass.newInstance();

                    String mappingValue = service.value();
                    //类的映射路径和类的实例化对象进行绑定
                    beans.put(mappingValue, instance);
                }
            } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

    private void scanPackage(String basePackage) {
        String path = basePackage.replaceAll("\\.", "/");
        //相对路径fileName前不加/
        URL url = getClass().getClassLoader().getResource(path);
        String filePath = url.getFile();
        //目录递归扫描
        File[] files = new File(filePath).listFiles();
        for (File file : files) {
            //判断是不是目录
            if (file.isDirectory()) {
                scanPackage(basePackage + "." + file.getName());
            } else {
                clazzNames.add(basePackage + "." + file.getName());
            }
        }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //super.doGet(req, resp);
        resp.getWriter().append("served at: ").append(req.getContextPath());
        doPost(req,resp);
    }

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

        String uri = req.getRequestURI();
        String contextPath = req.getContextPath();
        String path = uri.replace(contextPath, "");
        Method method = (Method)handlerMethod.get(path);
        Object instance = beans.get("/" + path.split("/")[1]);
        try {
            Object object = method.invoke(instance, null);
            System.out.println(object.toString());
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

TestService


package ai.ls.springmvc.service;

import ai.ls.springmvc.annotation.Service;

/**
 * @BelongsProject: Lspring
 * @BelongsPackage: ai.ls.springmvc.service
 * @Author: ls
 * @CreateTime: 2020-12-29 15:38
 * @Description: test service
 */
@Service("/testService")
public class TestService {

    public String test() {
        return "TestService.test()";
    }

}

TestController

package ai.ls.springmvc.controller;

import ai.ls.springmvc.annotation.Autowired;
import ai.ls.springmvc.annotation.Controller;
import ai.ls.springmvc.annotation.RequestMapping;
import ai.ls.springmvc.service.TestService;

/**
 * @BelongsProject: Lspring
 * @BelongsPackage: ai.ls.springmvc.controller
 * @Author: ls
 * @CreateTime: 2020-12-29 15:36
 * @Description: 测试入口
 */
@Controller
@RequestMapping("/test")
public class TestController {
    @Autowired("/testService")
    TestService testService;

    @RequestMapping("/index")
    public String index() {
        return "test springMVC:" + testService.test();
    }
}

web.xml


<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>ai.ls.springmvc.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>DispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

说明

本文参考云析擎天老司机

本文旨在模拟springmvc核心重点原理,实现过程不够优雅、但简单易懂

文章有错漏希望给与指出、共同探讨

项目源码gitee地址在前文已贴出,感兴趣的大佬可以在develop分支随意玩耍,提交内容小弟酌情合并到master

posted @ 2020-12-30 09:17  刘66  阅读(36)  评论(0编辑  收藏  举报