美文网首页Java学习笔记
自己手写一个SpringMVC框架-简单版

自己手写一个SpringMVC框架-简单版

作者: AmeeLove | 来源:发表于2018-03-13 20:27 被阅读97次

    项目结构

    image.png

    pom.xml

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>com.ghgcn</groupId>
        <artifactId>myspringmvc</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <packaging>war</packaging>
        <dependencies>
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <version>3.0.1</version>
                <scope>provided</scope>
            </dependency>
        </dependencies>
    
    
        <build>
            <finalName>myspringmvc</finalName>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.2</version>
                    <configuration>
                        <source>1.7</source>
                        <target>1.7</target>
                        <encoding>UTF-8</encoding>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>
    

    web.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://java.sun.com/xml/ns/javaee"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
        version="3.0">
    
    
        <!-- 前端控制器 -->
        <servlet>
            <servlet-name>MyDispatcherServlet</servlet-name>
            <servlet-class>com.ghgcn.myspringmvc.servlet.MyDispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <!--配置文件  -->
                <param-value>classpath:application.properties</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet-mapping>
            <servlet-name>MyDispatcherServlet</servlet-name>
            <url-pattern>/*</url-pattern>
        </servlet-mapping>
    
    
        <!--字符编码 -->
        <filter>
            <filter-name>UTF8Filter</filter-name>
            <filter-class>com.ghgcn.myspringmvc.filter.UTF8Filter</filter-class>
            <init-param>
                <param-name>encoding</param-name>
                <param-value>UTF-8</param-value>
            </init-param>
        </filter>
        <filter-mapping>
            <filter-name>UTF8Filter</filter-name>
            <url-pattern>/</url-pattern>
        </filter-mapping>
    </web-app>
    

    properties

    basePackage=com.ghgcn.myspringmvc
    

    注解

    /**
     * 控制器
     * @author Administrator
     *
     */
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface MyController {
    
        /**
         * 表示给controller注册别名
         * @return
         */
        String value() default "";
    }
    
    /**
     * 请求路径
     * @author Administrator
     *
     */
    @Target(value={ElementType.METHOD,ElementType.TYPE})
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyRequestMapping {
    
        /**
         * 表示访问该方法的url
         * @return
         */
        String value() default "";
    }
    
    
    /**
     * 参数
     * @author Administrator
     *
     */
    @Target(ElementType.PARAMETER)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface MyRequestParam {
    
        /**
         * 表示参数的别名,必填
         * @return
         */
        String value();
    }
    
    
    

    helper

    public class IOHelper {
    
        public static void close(AutoCloseable closeable) {
    
            try {
                if (closeable != null) {
    
                    closeable.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
    
        }
    }
    

    过滤器

    package com.ghgcn.myspringmvc.filter;
    
    import java.io.IOException;
    
    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletResponse;
    
    
    public class UTF8Filter implements Filter {
    
        private String encoding="UTF-8";
    
        /**
         * @see Filter#init(FilterConfig)
         */
        public void init(FilterConfig fConfig) throws ServletException {
            String initParameter = fConfig.getInitParameter("encoding");
            if(initParameter!=null){
                this.encoding=initParameter;
            }
            
        }
     
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            
            request.setCharacterEncoding(encoding);
            chain.doFilter(request, response);
            HttpServletResponse resp = (HttpServletResponse)response ; 
            resp.setCharacterEncoding(encoding);
        }
    
    
        
        @Override
        public void destroy() {
            
        }
    
    
    }
    
    

    MyDispatcherServlet 前端控制器

    package com.ghgcn.myspringmvc.servlet;
    
    import java.io.File;
    import java.io.IOException;
    import java.io.InputStream;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.net.URL;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.Map.Entry;
    import java.util.Properties;
    
    import javax.servlet.ServletConfig;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import com.ghgcn.myspringmvc.annotation.MyController;
    import com.ghgcn.myspringmvc.annotation.MyRequestMapping;
    import com.ghgcn.myspringmvc.helper.IOHelper;
    
    public class MyDispatcherServlet extends HttpServlet {
    
        /**
         * 
         */
        private static final long serialVersionUID = 7613183580743740653L;
        /**
         * 属性-加载配置文件
         */
        private Properties properties = new Properties();
    
        /**
         * 所有类的名称
         */
        private List<String> classNames = new ArrayList<>();
    
        /**
         * 注入的容器
         */
        private Map<String, Object> ioc = new HashMap<>();
    
        /**
         * 请求地址映射-controller-方法
         */
        private Map<String, Method> handlerMapping = new HashMap<>();
    
        /**
         * 控制器
         */
        private Map<String, Object> controllerMap = new HashMap<>();
    
        @Override
        public void init(ServletConfig config) throws ServletException {
            // 1.加载配置文件
            doLoadConfig(config.getInitParameter("contextConfigLocation"));
            // 2.初始化所有相关联的类,扫描用户设定的包下面所有的类 基础包下的所有类的全路径名称com.xx.xx.类名
            doScanner(properties.getProperty("basePackage"));
            // 3.拿到扫描到的类,通过反射机制,实例化,并且放到ioc容器中(k-v beanName-bean) beanName默认是首字母小写
            doInstance();
            // 4.初始化HandlerMapping(将url和method对应上)
            initHandlerMapping();
        }
    
        /**
         * 1.加载启 到的配置
         * 
         * @param location
         */
        private void doLoadConfig(String location) {
    
            System.out.println("location "+location);
            
            if(location.startsWith("classpath:")){
                location=location.substring(location.indexOf(":")+1);
            }
            /**
             * 加载文件 -application.properties
             */
            InputStream inStream = this.getClass().getClassLoader().getResourceAsStream(location);
    
            try {
                this.properties.load(inStream);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (inStream != null) {
                    IOHelper.close(inStream);
                }
            }
    
        }
    
        /**
         * 2.扫描包,基础包下的所有类的全路径名称com.xx.xx.类名
         * 
         * @param property
         */
        private void doScanner(String packageName) {
    
            // 1.把所有的.替换成/
            URL url = this.getClass().getClassLoader().getResource(packageName.replace(".", "/"));
            System.out.println("url  " + url.getFile());
            /**
             * 2.获取所有文件
             */
            File file = new File(url.getFile());
    
            for (File f : file.listFiles()) {
    
                if (f.isDirectory()) {
                    // 递归
                    doScanner(packageName + "." + f.getName());
                } else {
                    String className = packageName + "." + f.getName().replace(".class", "");
                    System.out.println("className " + className);
                    /**
                     * 反所有类都放在这个集合中-类的全路径
                     */
                    classNames.add(className);
                }
            }
    
        }
    
        /**
         * 3 拿到扫描到的类,通过反射机制,实例化,并且放到ioc容器中(k-v beanName-bean) beanName默认是首字母小写
         */
        private void doInstance() {
    
            if (classNames.isEmpty()) {
                return;
            }
    
            for (String classname : classNames) {
                
                try {
                    //反射来实例化(只有加@MyController需要实例化)
                    Class<?> clazz = Class.forName(classname);
    
                    if (clazz.isAnnotationPresent(MyController.class)) {
                        // 放入IOC容器 类首字母小写, 通过无参构造方法获取实例
                        ioc.put(toLowerFirstWord(clazz.getSimpleName()), clazz.newInstance());
                    } else {
                        continue;
                    }
    
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                    continue;
                } catch (InstantiationException e) {
                    e.printStackTrace();
                    continue;
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                    continue;
                }
            }
        }
    
        /**
         * 4.将请求的url与method中的方法对应
         */
        private void initHandlerMapping() {
    
            if (ioc.isEmpty()) {
                return;
            }
    
            try {
    
                for (Map.Entry<String, Object> entry : ioc.entrySet()) {
    
                    Class<? extends Object> clazz = entry.getValue().getClass();
    
                    /**
                     * 判断如果不是MyController 的注解,就下一个
                     */
                    if (!clazz.isAnnotationPresent(MyController.class)) {
                        continue;
                    }
    
                    // 拼url时,是controller头的url拼上方法上的url
                    String baseUrl = "";
                    if (clazz.isAnnotationPresent(MyRequestMapping.class)) {
                        MyRequestMapping contollerAnnotation = clazz.getAnnotation(MyRequestMapping.class);
                        baseUrl = contollerAnnotation.value();
                    }
    
                    /**
                     * 获取所有方法的请求myrequestiongmapping
                     */
    
                    Method[] methods = clazz.getMethods();
    
                    for (Method method : methods) {
    
                        if (!method.isAnnotationPresent(MyRequestMapping.class)) {
                            continue;
                        }
    
                        MyRequestMapping methodAnnotation = method.getAnnotation(MyRequestMapping.class);
    
                        /**
                         * 方法的请求路径
                         */
                        String methodUrl = methodAnnotation.value();
                        /**
                         * 拼接
                         */
                        String url = (baseUrl + "/" + methodUrl).replaceAll("/+", "/");
                        System.out.println("url  " + url + " method  " + method);
                        /**
                         * 将url与method做关联
                         */
                        handlerMapping.put(url, method);
    
                        /**
                         * 控制器 -- 请求地址与控制器关系
                         */
                        controllerMap.put(url, clazz.newInstance());
    
                    }
    
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
    
        }
    
        @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 {
    
            try {
                // 处理请求
                resp.setHeader("Content-type", "text/html;charset=UTF-8");  
                doDispatch(req, resp);
            } catch (Exception e) {
                resp.getWriter().write("500!! Server Exception");
            }
        }
    
        /**
         * 处理请求
         * 
         * @param req
         * @param resp
         * @throws IOException
         */
        private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    
            if (handlerMapping.isEmpty()) {
                return;
            }
    
            /**
             * 获取请求路径
             */
            String url = req.getRequestURI();
            /**
             * 上下路径
             */
            String contextPath = req.getContextPath();
    
            url = url.replace(contextPath, "").replaceAll("/+", "/");
    
            /**
             * 判断请求路径是否存在
             */
            if (!handlerMapping.containsKey(url)) {
                resp.getWriter().write("404 Not found");
                return;
            }
    
            /**
             * 获取请求的方法
             */
            Method method = handlerMapping.get(url);
    
            /**
             * 获取方法的参数
             */
            Class<?>[] parameterTypes = method.getParameterTypes();
            
    
            /**
             * 获取请求的方法的请求参数
             */
            Map<String, String[]> requestparameterMap = req.getParameterMap();
            /**
             * 保存参数值
             */
            Object[] paramValues = new Object[parameterTypes.length];
            
            /**
             * 方法的参数列表
             */
            for (int i = 0; i < paramValues.length; i++) {
    
                String requestParam = parameterTypes[i].getSimpleName();
                System.out.println("requestParam "+requestParam);
                /**
                 * 请求
                 */
                if (requestParam.equals("HttpServletRequest")) {
                    // 参数类型已明确,这边强转类型
                    paramValues[i] = req;
                    continue;
                }
                /**
                 * 响应
                 */
                if (requestParam.equals("HttpServletResponse")) {
                    paramValues[i] = resp;
                    continue;
                }
                
                /**
                 * 参数转换为
                 */
                if (requestParam.equals("String")) {
                    for (Entry<String, String[]> param : requestparameterMap.entrySet()) {
                        System.out.println("param "+param);
                        String value = Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "").replaceAll(",\\s", ",");
                        System.out.println("value "+value);
                        paramValues[i] = value;
                    }
                }
                
                /**
                 * 通过反射来执行方法
                 * /第一个参数是method所对应的实例 在ioc容器中
                 */
                
            }
            try {
                method.invoke(controllerMap.get(url), paramValues);
            } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
                e.printStackTrace();
            }
    
    
        }
    
        /**
         * 把字符串的首字母小写
         * 
         * @param name
         * @return
         */
        private String toLowerFirstWord(String name) {
            char[] charArray = name.toCharArray();
            charArray[0] += 32;
            return String.valueOf(charArray);
        }
    
    }
    
    

    测试

    package com.ghgcn.myspringmvc.action;
    
    import java.io.IOException;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import com.ghgcn.myspringmvc.annotation.MyController;
    import com.ghgcn.myspringmvc.annotation.MyRequestMapping;
    import com.ghgcn.myspringmvc.annotation.MyRequestParam;
    
    @MyController
    @MyRequestMapping("/test")
    public class TestController {
    
        @MyRequestMapping("/doTest")
        public void test1(HttpServletRequest request, HttpServletResponse response, @MyRequestParam("param") String param) {
            System.out.println("============test1=============");
            System.out.println(param);
            try {
                //response.setHeader("Content-type", "text/html;charset=UTF-8");  
                response.getWriter().write("doTest method success! param:" + param);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        @MyRequestMapping("/doTest2")
        public void test2(HttpServletRequest request, HttpServletResponse response,String test) {
            System.out.println("==========test2=======");
            System.out.println(test);
            try {
                //response.setHeader("Content-type", "text/html;charset=UTF-8");  
                response.getWriter().write("doTest2 method success!");
                response.getWriter().write(test);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    
    image.png
    image.png

    相关文章

      网友评论

        本文标题:自己手写一个SpringMVC框架-简单版

        本文链接:https://www.haomeiwen.com/subject/mqetqftx.html