zoukankan      html  css  js  c++  java
  • 手写 Spring MVC

    手写 Spring MVC

    不多说,简历装 X 必备。不过练好还是需要求一定的思维能力。

    一、整体思路

    思路要熟练背下来

    1)配置阶段

    • 配置 web.xml: XDispatchServlet
    • 设定 init-param: contextConfigLocation = classpath:application.xml
    • 设定 url-pattern: /*
    • 配置 Annotation: @XController @XService @XAutowired @XRequestMapping

    2)初始化阶段

    • IOC:
      • 调用 init() 方法: 加载配置文件
      • IOC 容器初始化: Map<String, Object>
      • 扫描相关的类: scan-package="com.xiaopengwei"
      • 创建实例化并保存到容器: 同过反射机制将类实例化放入 IOC 容器中
    • DI:
      • 进行 DI 操作: 扫描 IOC 容器中的实例,给没有赋值的属性自动赋值
    • MVC:
      • 初始化 HandlerMapping: 将一个 URL 和一个 Method 进行一对一的关联映射 Map<String, Method>

    3)运行阶段

    • 调用 doGet() / doPost() 方法: Web 容器调用 doGet() / doPost() 方法,获得 request/response 对象
    • 匹配 HandleMapping: 从 request 对象中获得用户输入的 url,找到其对应的 Method
    • 反射调用 method.invoker(): 利用反射调用方法并返回结果
    • response.getWrite().write(): 将返回结果输出到浏览器

    二、源代码

    项目结构:

    源代码:

    (1)在 pom.xml 引入一个 jar 包

    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
    </dependency>
    

    (2)web.xml 文件:

    <!DOCTYPE web-app PUBLIC
            "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
            "http://java.sun.com/dtd/web-app_2_3.dtd" >
    
    <web-app>
        <display-name>Archetype Created Web Application</display-name>
        <servlet>
            <servlet-name>xmvc</servlet-name>
            <servlet-class>com.xiaopengwei.xspring.servlet.XDispatchServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <!--you can't use classpath*: -->
                <param-value>application.properties</param-value>
            </init-param>
        </servlet>
        <servlet-mapping>
            <servlet-name>xmvc</servlet-name>
            <url-pattern>/*</url-pattern>
        </servlet-mapping>
    </web-app>
    

    (3)application.properties 文件:

    scan-package=com.xiaopengwei
    

    (4)自定义注解 XAutowired:

    package com.xiaopengwei.xspring.annotation;
    import java.lang.annotation.*;
    /**
     * <p>
     *
     * @author XiaoPengwei
     * @since 2019-07-19
     */
    @Target({ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface XAutowired {
        String value() default "";
    }
    

    (5)自定义注解 XController:

    package com.xiaopengwei.xspring.annotation;
    import java.lang.annotation.*;
    /**
     * <p>
     *
     * @author XiaoPengwei
     * @since 2019-07-19
     */
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface XController {
        String value() default "";
    }
    

    (6)自定义注解 XRequestMapping:

    package com.xiaopengwei.xspring.annotation;
    import java.lang.annotation.*;
    /**
     * <p>
     *
     * @author XiaoPengwei
     * @since 2019-07-19
     */
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface XRequestMapping {
        String value() default "";
    }
    

    (7)自定义注解 XService:

    package com.xiaopengwei.xspring.annotation;
    import java.lang.annotation.*;
    /**
     * <p>
     *
     * @author XiaoPengwei
     * @since 2019-07-19
     */
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface XService {
        String value() default "";
    }
    

    (8)核心 XDispatchServlet:

    package com.xiaopengwei.xspring.servlet;
    import com.xiaopengwei.xspring.annotation.XAutowired;
    import com.xiaopengwei.xspring.annotation.XController;
    import com.xiaopengwei.xspring.annotation.XRequestMapping;
    import com.xiaopengwei.xspring.annotation.XService;
    import javax.servlet.ServletConfig;
    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.io.InputStream;
    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.net.URL;
    import java.util.*;
    
    /**
     * <p>
     * XSpring
     *
     * @author XiaoPengwei
     * @since 2019-07-19
     */
    public class XDispatchServlet extends HttpServlet {
    
        /**
         * 属性配置文件
         */
        private Properties contextConfig = new Properties();
    
        private List<String> classNameList = new ArrayList<>();
    
        /**
         * IOC 容器
         */
        Map<String, Object> iocMap = new HashMap<String, Object>();
    
        Map<String, Method> handlerMapping = new HashMap<String, Method>();
    
        @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 {
    
            //7、运行阶段
            try {
                doDispatch(req, resp);
            } catch (Exception e) {
                e.printStackTrace();
                resp.getWriter().write("500 Exception Detail:
    " + Arrays.toString(e.getStackTrace()));
            }
    
        }
    
        /**
         * 7、运行阶段,进行拦截,匹配
         *
         * @param req  请求
         * @param resp 响应
         */
        private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws InvocationTargetException, IllegalAccessException {
    
            String url = req.getRequestURI();
    
            String contextPath = req.getContextPath();
    
            url = url.replaceAll(contextPath, "").replaceAll("/+", "/");
    
            System.out.println("[INFO-7] request url-->" + url);
    
            if (!this.handlerMapping.containsKey(url)) {
                try {
                    resp.getWriter().write("404 NOT FOUND!!");
                    return;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    
            Method method = this.handlerMapping.get(url);
    
            System.out.println("[INFO-7] method-->" + method);
    
    
            String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
    
            System.out.println("[INFO-7] iocMap.get(beanName)->" + iocMap.get(beanName));
    
            // 第一个参数是获取方法,后面是参数,多个参数直接加,按顺序对应
            method.invoke(iocMap.get(beanName), req, resp);
    
            System.out.println("[INFO-7] method.invoke put {" + iocMap.get(beanName) + "}.");
        }
    
        @Override
        public void init(ServletConfig servletConfig) throws ServletException {
    
            //1、加载配置文件
            doLoadConfig(servletConfig.getInitParameter("contextConfigLocation"));
    
            //2、扫描相关的类
            doScanner(contextConfig.getProperty("scan-package"));
    
            //3、初始化 IOC 容器,将所有相关的类实例保存到 IOC 容器中
            doInstance();
    
            //4、依赖注入
            doAutowired();
    
            //5、初始化 HandlerMapping
            initHandlerMapping();
    
            System.out.println("XSpring FrameWork is init.");
    
            //6、打印数据
            doTestPrintData();
        }
    
        /**
         * 6、打印数据
         */
        private void doTestPrintData() {
    
            System.out.println("[INFO-6]----data------------------------");
    
            System.out.println("contextConfig.propertyNames()-->" + contextConfig.propertyNames());
    
            System.out.println("[classNameList]-->");
            for (String str : classNameList) {
                System.out.println(str);
            }
    
            System.out.println("[iocMap]-->");
            for (Map.Entry<String, Object> entry : iocMap.entrySet()) {
                System.out.println(entry);
            }
    
            System.out.println("[handlerMapping]-->");
            for (Map.Entry<String, Method> entry : handlerMapping.entrySet()) {
                System.out.println(entry);
            }
    
            System.out.println("[INFO-6]----done-----------------------");
    
            System.out.println("====启动成功====");
            System.out.println("测试地址:http://localhost:8080/test/query?username=xiaopengwei");
            System.out.println("测试地址:http://localhost:8080/test/listClassName");
        }
    
        /**
         * 5、初始化 HandlerMapping
         */
        private void initHandlerMapping() {
    
            if (iocMap.isEmpty()) {
                return;
            }
    
            for (Map.Entry<String, Object> entry : iocMap.entrySet()) {
                Class<?> clazz = entry.getValue().getClass();
    
                if (!clazz.isAnnotationPresent(XController.class)) {
                    continue;
                }
    
                String baseUrl = "";
    
                if (clazz.isAnnotationPresent(XRequestMapping.class)) {
                    XRequestMapping xRequestMapping = clazz.getAnnotation(XRequestMapping.class);
                    baseUrl = xRequestMapping.value();
                }
    
                for (Method method : clazz.getMethods()) {
                    if (!method.isAnnotationPresent(XRequestMapping.class)) {
                        continue;
                    }
    
                    XRequestMapping xRequestMapping = method.getAnnotation(XRequestMapping.class);
    
                    String url = ("/" + baseUrl + "/" + xRequestMapping.value()).replaceAll("/+", "/");
    
                    handlerMapping.put(url, method);
    
                    System.out.println("[INFO-5] handlerMapping put {" + url + "} - {" + method + "}.");
    
                }
            }
    
        }
    
        /**
         * 4、依赖注入
         */
        private void doAutowired() {
            if (iocMap.isEmpty()) {
                return;
            }
    
            for (Map.Entry<String, Object> entry : iocMap.entrySet()) {
    
                Field[] fields = entry.getValue().getClass().getDeclaredFields();
    
                for (Field field : fields) {
                    if (!field.isAnnotationPresent(XAutowired.class)) {
                        continue;
                    }
    
                    System.out.println("[INFO-4] Existence XAutowired.");
    
                    // 获取注解对应的类
                    XAutowired xAutowired = field.getAnnotation(XAutowired.class);
                    String beanName = xAutowired.value().trim();
    
                    // 获取 XAutowired 注解的值
                    if ("".equals(beanName)) {
                        System.out.println("[INFO] xAutowired.value() is null");
                        beanName = field.getType().getName();
                    }
    
                    // 只要加了注解,都要加载,不管是 private 还是 protect
                    field.setAccessible(true);
    
                    try {
                        field.set(entry.getValue(), iocMap.get(beanName));
    
                        System.out.println("[INFO-4] field set {" + entry.getValue() + "} - {" + iocMap.get(beanName) + "}.");
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
        /**
         * 3、初始化 IOC 容器,将所有相关的类实例保存到 IOC 容器中
         */
        private void doInstance() {
            if (classNameList.isEmpty()) {
                return;
            }
    
            try {
                for (String className : classNameList) {
    
                    Class<?> clazz = Class.forName(className);
    
                    if (clazz.isAnnotationPresent(XController.class)) {
                        String beanName = toLowerFirstCase(clazz.getSimpleName());
                        Object instance = clazz.newInstance();
    
                        // 保存在 ioc 容器
                        iocMap.put(beanName, instance);
                        System.out.println("[INFO-3] {" + beanName + "} has been saved in iocMap.");
    
                    } else if (clazz.isAnnotationPresent(XService.class)) {
    
                        String beanName = toLowerFirstCase(clazz.getSimpleName());
    
                        // 如果注解包含自定义名称
                        XService xService = clazz.getAnnotation(XService.class);
                        if (!"".equals(xService.value())) {
                            beanName = xService.value();
                        }
    
                        Object instance = clazz.newInstance();
                        iocMap.put(beanName, instance);
                        System.out.println("[INFO-3] {" + beanName + "} has been saved in iocMap.");
    
                        // 找类的接口
                        for (Class<?> i : clazz.getInterfaces()) {
                            if (iocMap.containsKey(i.getName())) {
                                throw new Exception("The Bean Name Is Exist.");
                            }
    
                            iocMap.put(i.getName(), instance);
                            System.out.println("[INFO-3] {" + i.getName() + "} has been saved in iocMap.");
                        }
                    }
    
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 获取类的首字母小写的名称
         *
         * @param className ClassName
         * @return java.lang.String
         */
        private String toLowerFirstCase(String className) {
            char[] charArray = className.toCharArray();
            charArray[0] += 32;
            return String.valueOf(charArray);
        }
    
        /**
         * 2、扫描相关的类
         *
         * @param scanPackage properties --> scan-package
         */
        private void doScanner(String scanPackage) {
    
            // package's . ==> /
            URL resourcePath = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\.", "/"));
    
            if (resourcePath == null) {
                return;
            }
    
            File classPath = new File(resourcePath.getFile());
    
            for (File file : classPath.listFiles()) {
    
                if (file.isDirectory()) {
    
                    System.out.println("[INFO-2] {" + file.getName() + "} is a directory.");
    
                    // 子目录递归
                    doScanner(scanPackage + "." + file.getName());
    
                } else {
    
                    if (!file.getName().endsWith(".class")) {
                        System.out.println("[INFO-2] {" + file.getName() + "} is not a class file.");
                        continue;
                    }
    
                    String className = (scanPackage + "." + file.getName()).replace(".class", "");
    
                    // 保存在内容
                    classNameList.add(className);
    
                    System.out.println("[INFO-2] {" + className + "} has been saved in classNameList.");
                }
            }
        }
    
        /**
         * 1、加载配置文件
         *
         * @param contextConfigLocation web.xml --> servlet/init-param
         */
        private void doLoadConfig(String contextConfigLocation) {
    
            InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
    
            try {
                // 保存在内存
                contextConfig.load(inputStream);
    
                System.out.println("[INFO-1] property file has been saved in contextConfig.");
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (null != inputStream) {
                    try {
                        inputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    

    (9)示例:TestController:

    package com.xiaopengwei.demo.xcontroller;
    import com.xiaopengwei.demo.xservice.ITestXService;
    import com.xiaopengwei.xspring.annotation.XAutowired;
    import com.xiaopengwei.xspring.annotation.XController;
    import com.xiaopengwei.xspring.annotation.XRequestMapping;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.util.List;
    
    /**
     * <p>
     * 前置控制器
     *
     * @author XiaoPengwei
     * @since 2019-07-19
     */
    @XController
    @XRequestMapping("/test")
    public class TestController {
    
        @XAutowired
        ITestXService testXService;
    
        /**
         * 测试方法 /test/query
         *
         * @param req  请求体
         * @param resp 响应体
         */
        @XRequestMapping("/query")
        public void query(HttpServletRequest req, HttpServletResponse resp) {
    
            if (req.getParameter("username") == null) {
               try {
                    resp.getWriter().write("param username is null");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            } else {
    
                String paramName = req.getParameter("username");
                try {
                    resp.getWriter().write("param username is " + paramName);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                System.out.println("[INFO-req] New request param username-->" + paramName);
            }
        }
    
        /**
         * 测试方法 /test/listClassName
         *
         * @param req  请求体
         * @param resp 响应体
         */
        @XRequestMapping("/listClassName")
        public void listClassName(HttpServletRequest req, HttpServletResponse resp) {
            String str = testXService.listClassName();
            System.out.println("testXService----------=-=-=>" + str);
            try {
                resp.getWriter().write(str);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    (10)示例接口:ITestXService:

    package com.xiaopengwei.demo.xservice;
    /**
     * <p>
     * 接口
     *
     * @author XiaoPengwei
     * @since 2019-07-19
     */
    public interface ITestXService {
        String listClassName();
    }
    

    (11)示例实现类 TestXServiceImpl:

    package com.xiaopengwei.demo.xservice.impl;
    import com.xiaopengwei.demo.xservice.ITestXService;
    import com.xiaopengwei.xspring.annotation.XService;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * <p>
     * 业务实现类
     *
     * @author XiaoPengwei
     * @since 2019-07-19
     */
    @XService
    public class TestXServiceImpl implements ITestXService {
    
        @Override
        public String listClassName() {
    
            // 假装来自数据库
            return "123456TestXServiceImpl";
        }
    }
    

    (12)测试:

    配置 Tomcat 后,访问:

    http://localhost:8080/test/query?username=xiaopengwei
    

    http://localhost:8080/test/listClassName
    

  • 相关阅读:
    前端安全之XSS攻击
    从JavaScript执行上下文理解变量提升
    em、rem和px的区别
    纯CSS实现幻灯片效果
    小白在使用ISE编写verilog代码综合时犯得错误及我自己的解决办法
    结构体指针中的一点困惑
    xilinx fpga中块ram的使用——简单双端口ram的使用
    在模块中如何去写输出标志位的程序
    学习感悟
    xilinx fpga 生成3*3窗口
  • 原文地址:https://www.cnblogs.com/xpwi/p/11219083.html
Copyright © 2011-2022 走看看