专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

尝试手写简单 SpringMVC

大体思路

1.配置阶段

  • 配置 web.xml
  • 配置 application.properties 配置文件,主要配置需要扫描的包路径
  • 配置 Init-pattern
  • 配置 @Autowired,@Controller 等注解

2、初始化阶段

  • 调用 init() 方法,加载配置文件
  • IOC 容器进行初始化 :Map<String,Object>
  • 根据配置对包中类进行扫描
  • 创建类实例
  • 实现 DI 依赖注入
  • HandleMapping 容器初始化 : Map<String,Method>
  • 路径和方法进行映射

3、执行阶段

  • doGet()/doPost()
  • 在 HandleMapping 中匹配路径
  • 利用反射调用 invoke() 执行
  • response 返回结果

简单代码

配置阶段

  • web.xml
<web-app>
    <display-name>Archetype Created Web Application</display-name>

    <servlet>
        <servlet-name>myServlet</servlet-name>
        <servlet-class>servlet.MyServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>application.properties</param-value>
        </init-param>
    </servlet>

    <servlet-mapping>
        <servlet-name>myServlet</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>

</web-app>

配置 servlet-class 时,所写路径的类,要继承 HttpServlet

  • application.properties
    scanPackage = com.stg.myspring

  • annotioned
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GPAutowired {
    String value() default "";
}

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

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

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

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

初始化阶段

    // 存储 Config 配置
    Properties config = new Properties();

    // 包下面所有的类型
    List<String> classNameList = new ArrayList<String>();

    // IOC 容器
    Map<String, Object> iocMap = new HashMap<String, Object>();

    // handleMapping 容器
    Map<String, Method> handleMap = new HashMap<String, Method>();

  • 加载 web.xml
/**
     * 加载配置文件
     *
     * @param contextConfigLocation
     */
    private void doLoadConfig(String contextConfigLocation) {
        InputStream webInput = null;
        try {
            // 通过 classLoader 进行加载
            webInput = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
            config.load(webInput);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

  • 扫描包路径
private void doScanPackage(String scanPackage) {
        // 磁盘存储是 "/" 所以将 "." 进行替换
        URL url = this.getClass().getClassLoader().getResource(scanPackage
                .replaceAll("\\.", "/"));
        File classFile = new File(url.getFile());
        for (File file : classFile.listFiles()) {
            if (file.isDirectory()) {
                // 如果 file 是文件夹,进行递归
                this.doScanPackage(scanPackage + "." + file.getName());
            } else {
                if (!file.getName().endsWith(".class")) {
                    continue;
                }
                String className = (scanPackage + "." + file.getName()).replaceAll(".class", "");
                // 将包下类名进行存储
                classNameList.add(className);
            }
        }
    }

  • 初始化 IOC 容器
private void doInitIOCMap() {
        for (String className : classNameList) {
            try {
                Class<?> clazz = Class.forName(className);
                if (clazz.isAnnotationPresent(MyController.class)) {
                    Object object = clazz.newInstance();
                    String beanName = this.toLowerName(clazz.getSimpleName());
                    iocMap.put(beanName, object);
                } else if (clazz.isAnnotationPresent(MyService.class)) {
                    String beanName = "";
                    beanName = this.toLowerName(clazz.getSimpleName());
                    String annoName = clazz.getAnnotation(MyService.class).value();
                    if (!"".equals(annoName)) {
                        beanName = annoName;
                    }
                    Object object = clazz.newInstance();
                    iocMap.put(beanName, object);
                    for (Class<?> i : clazz.getInterfaces()) {
                        if (iocMap.containsKey(i.getName())) {
                            throw new Exception("The beanName is exists!!");
                        }
                        iocMap.put(i.getName(), object);
                    }
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

  • 注入
private void autoDI() {
        if (iocMap.isEmpty()) {
            return;
        }
        for (Map.Entry<String, Object> entry : iocMap.entrySet()) {
            // 获取到所有的属性
            Field[] declaredFields = entry.getValue().getClass().getDeclaredFields();
            for (Field field : declaredFields) {
                if (!field.isAnnotationPresent(MyAutowired.class)) {
                    continue;
                } else {
                    String beanName = "";
                    beanName = field.getAnnotation(MyAutowired.class).value().trim();
                    if ("".equals(beanName)) {
                        // 按类型注入
                        beanName = field.getType().getName();
                    }
                    field.setAccessible(true);
                    try {
                        field.set(entry.getValue(), iocMap.get(beanName));
                    } catch (Exception e) {
                        e.printStackTrace();
                        continue;
                    }
                }
            }
        }
    }

  • 初始化 HandleMapping
private void doUrlAndMethod() {
        for (Map.Entry<String, Object> entry : iocMap.entrySet()) {
            Class<?> clazz = entry.getValue().getClass();
            if (clazz.isAnnotationPresent(MyRequestMapping.class)) {
                String path = clazz.getAnnotation(MyRequestMapping.class).value().trim();
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {
                    String methodPath = "";
                    if (method.isAnnotationPresent(MyRequestMapping.class)) {
                        methodPath = method.getAnnotation(MyRequestMapping.class).value().trim();
                    }
                    if (!"".equals(methodPath)) {
                        String handleMethod = "/" + path + "/" + methodPath;
                        handleMap.put(handleMethod.replaceAll("/+", "/"), method);
                    }
                }
            }
        }
    }

执行阶段

/**
     * 进行路径匹配,执行方法
     *
     * @param req
     * @param resp
     */
    private void invokeMethod(HttpServletRequest req, HttpServletResponse resp) throws IOException, InvocationTargetException, IllegalAccessException {
        // 绝对路径
        String url = req.getRequestURI();
        // 相对路径
        String contextPath = req.getContextPath();
        url = url.replaceAll(contextPath, "").replaceAll("/+", "/");
        if (!handleMap.containsKey(url)) {
            resp.getWriter().println("404 Page not found");
        } else {
            Method method = handleMap.get(url);
            //获取方法的形参列表
            Class<?>[] parameterTypes = method.getParameterTypes();
            //保存请求的url参数列表
            Map<String, String[]> parameterMap = req.getParameterMap();
            //保存赋值参数的位置
            Object[] paramValues = new Object[parameterTypes.length];
            //按根据参数位置动态赋值
            for (int i = 0; i < parameterTypes.length; i++) {
                Class parameterType = parameterTypes[i];
                if (parameterType == HttpServletRequest.class) {
                    paramValues[i] = req;
                    continue;
                } else if (parameterType == HttpServletResponse.class) {
                    paramValues[i] = resp;
                    continue;
                } else if (parameterType == String.class) {

                    //提取方法中加了注解的参数
                    Annotation[][] pa = method.getParameterAnnotations();
                    for (int j = 0; j < pa.length; j++) {
                        for (Annotation a : pa[i]) {
                            if (a instanceof GPRequestParam) {
                                String paramName = ((GPRequestParam) a).value();
                                if (!"".equals(paramName.trim())) {
                                    String value = Arrays.toString(parameterMap.get(paramName))
                                            .replaceAll("\\[|\\]", "")
                                            .replaceAll("\\s", ",");
                                    paramValues[i] = value;
                                }
                            }
                        }
                    }

                }
            }
            //投机取巧的方式
            //通过反射拿到method所在class,拿到class之后还是拿到class的名称
            //再调用toLowerFirstCase获得beanName
            String beanName = this.toLowerName(method.getDeclaringClass().getSimpleName());
            method.invoke(iocMap.get(beanName), new Object[]{req, resp, parameterMap.get("name")[0]});
        }
    }

文章永久链接:https://tech.souyunku.com/42847

未经允许不得转载:搜云库技术团队 » 尝试手写简单 SpringMVC

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们