zoukankan      html  css  js  c++  java
  • 手写springmvc

    手写springmvc

    既然已经手写了spring的IOC,那springmvc肯定也要尝试写写了。手写spring博客:https://www.cnblogs.com/xiaojiesir/p/11139203.html

    SpringMVC的运行流程:

    (1)首先浏览器发送请求——>DispatcherServlet,前端控制器收到请求后自己不进行处理,而是委托给其他的解析器进行处理,作为统一访问点,进行全局的流程控制;

    (2)DispatcherServlet——>HandlerMapping,处理器映射器将会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器对象、多个HandlerInterceptor拦截器)对象;

    (3)DispatcherServlet——>HandlerAdapter,处理器适配器将会把处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持很多类型的处理器;

    (4)HandlerAdapter——>调用处理器相应功能处理方法,并返回一个ModelAndView对象(包含模型数据、逻辑视图名);

    (5)ModelAndView对象(Model部分是业务对象返回的模型数据,View部分为逻辑视图名)——> ViewResolver, 视图解析器将把逻辑视图名解析为具体的View;

    (6)View——>渲染,View会根据传进来的Model模型数据进行渲染,此处的Model实际是一个Map数据结构;

    (7)返回控制权给DispatcherServlet,由DispatcherServlet返回响应给用户,到此一个流程结束。

    梳理SpringMVC的设计思路

    在没有使用框架前,我们是使用servlet来实现前后端交互的,所以springmvc说到底还是在servlet基础上展开的。如果对servlet不是很熟悉的话,需要先学习下servlet。

    框架只是方便开发,底层知识才是最重要的。在掌握底层知识的基础上,再学习框架的设计理念来成长自己。

    Servlet 生命周期

    Servlet 生命周期可被定义为从创建直到毁灭的整个过程。以下是 Servlet 遵循的过程:

    • Servlet 通过调用 init () 方法进行初始化。
    • Servlet 调用 service() 方法来处理客户端的请求。
    • Servlet 通过调用 destroy() 方法终止(结束)。
    • 最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的。

    根据servlet生命周期来看,我们可以在init()方法中初始化基本的beans,并实例化controller层中的定义的service的变量,同时实现映射URL请求的Path和方法。

    而service方法我们用doget方法和dopost方法。doPost()主要是实现参数的解析,并通过反射的机制实现方法的调用。

    这里只粘贴了部分代码,具体代码可以看下:https://github.com/xiaojiesir/handwritingspringmvc

    项目结构

    配置web.xml

    使用servlet前,我们需要再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>mvc</servlet-name>
          <servlet-class>com.springframework.servlet.DispatcherServlet</servlet-class>
          <init-param>
              <param-name>contextConfigLocation</param-name>
              <param-value>application.properties</param-value>
          </init-param>
          <!-- Servlet 就会在服务器启动 时执行了。(注意:如果设置为负整数或者不配置,则不会在启动 服务器时执行,而要等此Servlet 被调用时才会被执行。 )-->
          <load-on-startup>1</load-on-startup>
      </servlet>
      <servlet-mapping>
          <servlet-name>mvc</servlet-name>
          <url-pattern>/*</url-pattern>
      </servlet-mapping>
    
    </web-app>

    <url-pattern>/*</url-pattern> 这种拦截匹配规则其实是一种错误方式,因为只是简易springmvc框架,不需要jsp,所以没问题。具体可以百度了解这方面知识。

    实现init方法

        @Override
        public void init(ServletConfig config) throws ServletException {
            System.out.println("init方法");
            //1.加载配置文件
            doLoadCongig(config.getInitParameter(LOCATION));
            //2.扫描所有相关的类
            doScanner(p.getProperty("scanPackage"));
            //3.初始化所有的相关类的实例,并保存到IOC容器中
            doInstance();
            //4.依赖注入
            doAutowired();
            //5.构造HandlerMapping
            initHandlerMapping();
            //6.等待请求,匹配URL,定位方法,反射调用执行
            //调用doGet或者doPost方法
            //提示信息
            System.out.println("my springmvc is success");
        }

    init方法中的1.2.3.4可以参考之前手写spring中的代码,我详细讲解下5

    第五步遍历容器中的bean,利用@MyRequestMapping注解将url与方法做映射

        private void initHandlerMapping() {
            if (ioc.isEmpty()) {
                return;
            }
            try {
                for (Entry<String, Object> entry : ioc.entrySet()) {
                    Class<? extends Object> clazz = entry.getValue().getClass();
                    if (!clazz.isAnnotationPresent(MyController.class)) {
                        continue;
                    }
    
                    // 拼url时,是controller头的url拼上方法上的url
                    String baseUrl = "";
                    if (clazz.isAnnotationPresent(MyRequestMapping.class)) {
                        MyRequestMapping annotation = clazz.getAnnotation(MyRequestMapping.class);
                        baseUrl = annotation.value();
                    }
                    Method[] methods = clazz.getMethods();
                    for (Method method : methods) {
                        if (!method.isAnnotationPresent(MyRequestMapping.class)) {
                            continue;
                        }
                        MyRequestMapping annotation = method.getAnnotation(MyRequestMapping.class);
                        String url = annotation.value();
    
                        url = (baseUrl + "/" + url).replaceAll("/+", "/");
                        handlerMapping.put(url, method);
                        System.out.println(url + "," + method);
                    }
    
                }
    
            } catch (Exception e) {
                e.printStackTrace();
            }
    
        }

    实现dopost方法

    根据请求url,获取method,根据@MyRequestParam注解获取方法参数并赋值,利用反射执行方法

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            // TODO Auto-generated method stub
            try {
                System.out.println("doPost");
                doDispatch(req,resp);//未采用策略模式
                //doDispatchDesignPatterns(req,resp);//采用策略模式
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    }

    未采用策略模式,参数类型使用if else if判断类型,

        private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
            // TODO Auto-generated method stub
            if(this.handlerMapping.isEmpty()){
                return;
            }
            String url =req.getRequestURI();//   /myspringmvc/demo/user
            String contextPath = req.getContextPath();//   /myspringmvc
            url = url.replace(contextPath, "").replaceAll("/+", "/");
            if(!this.handlerMapping.containsKey(url)){
                resp.getWriter().write("404 Not Found!!");
                return;
            }
            Map<String,String[]> params = req.getParameterMap();
            Method method =this.handlerMapping.get(url);
            //获取方法的参数列表
            Class<?>[] paramerterTypes = method.getParameterTypes();
            //获取请求的参数
            Map<String,String[]> parameterMap  = req.getParameterMap();
            //保留参数值
            Object[] paramValues = new Object[paramerterTypes.length];
            //方法的参数列表
            for (int i = 0; i < paramerterTypes.length; i++) {
                //根据参数名称,做某些处理
                Class parameterType =paramerterTypes[i];
                if(parameterType == HttpServletRequest.class){
                    //参数类型已明确,这边强转类型
                    paramValues[i] =req;
                    continue;
                }else if(parameterType == HttpServletResponse.class){
                    paramValues[i] = resp;
                    continue;
                }else if(parameterType == String.class){
                    //获取当前方法的参数
                    Annotation[][] an = method.getParameterAnnotations();//个数和paramerterTypes.length一样
                    Annotation[] paramAns = an[i];
                    
                    for (Annotation paramAn : paramAns) {
                        //判断传进的paramAn.getClass()是不是 MyRequestParam 类型
                        if (MyRequestParam.class.isAssignableFrom(paramAn.getClass())) {
                            MyRequestParam cr = (MyRequestParam) paramAn;
                            String value = cr.value();
                            paramValues[i] = req.getParameter(value);
                        }
                    }
                }
                
            }
            try {
                //String beanName = lowerFirstCase(method.getDeclaringClass().getSimpleName());//获取源代码中给出的‘底层类’简称
                String beanName = "/" + url.split("/")[1];
                method.invoke(this.ioc.get(beanName), paramValues);
            } catch (Exception e) {
                // TODO: handle exception
                e.printStackTrace();
            }
            
         }

    采用策略模式

        private void doDispatchDesignPatterns(HttpServletRequest req, HttpServletResponse resp) {
            // 通过req获取请求的url /myspringmvc/demo/user
                String url = req.getRequestURI();
                // /myspringmvc
                String context = req.getContextPath();
                // /demo/user
                String path = url.replaceAll(context, "");
                
                // 通过当前的path获取handlerMap的方法名
                Method method = this.handlerMapping.get(path);
                // 获取beans容器中的bean
                Object instance = this.ioc.get("/" + path.split("/")[1]);// 处理参数
                HandlerAdapterService ha = (HandlerAdapterService) this.ioc.get("myHandlerAdapter"); 
                Object[] args = ha.handle(req, resp, method, ioc);
                
                // 通过反射来实现方法的调用
                try {
                    method.invoke(instance, args);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (IllegalArgumentException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
    
        }

    处理参数接口

    package com.xiaojiesir.demo.handlerAdapter;
    
    import java.lang.reflect.Method;
    import java.util.Map;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public interface HandlerAdapterService {
    
        public Object[] handle(HttpServletRequest req, HttpServletResponse resp,
                Method method, Map<String, Object> beans);
    }

    处理参数的实现类

    import java.lang.reflect.Method;
    import java.util.HashMap;
    import java.util.Map;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import com.springframework.annotation.MyService;
    import com.xiaojiesir.demo.argumentResolver.ArgumentResolver;
    
    @MyService("myHandlerAdapter")
    public class MyHandlerAdapter implements HandlerAdapterService {
    
        @Override
        public Object[] handle(HttpServletRequest req, HttpServletResponse resp, Method method, Map<String, Object> beans) {
            // TODO Auto-generated method stub
            //获取方法中含义的参数
                    Class<?>[] paramClazzs = method.getParameterTypes();
                    System.out.println("======当前需要解析的参数对应的类=========");
                    for(Class<?> clazz: paramClazzs) {
                        System.out.println(clazz);
                    }
                    
                    // 定义一个返回参数的结果集
                    Object[] args = new Object[paramClazzs.length];
    //                Object[] args = {req, resp, "name", "xiaojiesir"};
                    
                    // 定义一个ArgumentResolver实现类的Map
                    Map<String, Object> argumentResolvers = getBeansOfType(beans, ArgumentResolver.class);
                    System.out.println("======当前需要解析的参数对应的类实例化=========");
                    for(Map.Entry<String, Object> map: argumentResolvers.entrySet()) {
                        System.out.println("key:" + map.getKey() + "; value:" + map.getValue());
                    }
                    
                    //定义参数索引
                    int paramIndex = 0;
                    //定义数组下标索引
                    int i = 0; 
                    // 开始处理参数
                    for(Class<?> paramClazz: paramClazzs) {
                        //哪个参数对应了哪个参数解析类,用策略模式来找
                        for (Map.Entry<String, Object> entry : argumentResolvers.entrySet()) {
                            ArgumentResolver ar = (ArgumentResolver)entry.getValue();
                            
                            if (ar.support(paramClazz, paramIndex, method)) {
                                args[i++] = ar.argumentResolver(req,
                                        resp,
                                        paramClazz,
                                        paramIndex,
                                        method);
                            }
                        }
                        paramIndex++;
                    }
                    
                    return args;
                }
             
                /**
                 * @param beans IOC容器中全部的bean
                 * @param intfType 定义的ArgumentResolver类
                 * @return
                 */
                private Map<String, Object> getBeansOfType(Map<String, Object> beans,
                        Class<ArgumentResolver> intfType) {
                    Map<String, Object> resultBeans = new HashMap<>();
                    for(Map.Entry<String, Object> map: beans.entrySet()) {
                        // 获取满足ArgumentResolver接口的bean
                        Class<?>[] intfs = map.getValue().getClass().getInterfaces();
                        if(intfs != null && intfs.length >0) {
                            for(Class<?> intf: intfs) {
                                // 将满足的bean存储在resultBeans中
                                if(intf.isAssignableFrom(intfType)) {
                                    resultBeans.put(map.getKey(), map.getValue());
                                }
                            }
                        }
                    }
                    return resultBeans;
                }
    
        }

    参数接口

    import java.lang.reflect.Method;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public interface ArgumentResolver {
        /**
         * 判断当前的类是继承于ArgumentResolver
         * @param type 当前参数注解的类对象
         * @param paramIndex 参数下标
         * @param method 当前的方法
         * @return
         */
        public boolean support(Class<?> type, int paramIndex, Method method);
        
        /**
         * 
         * @param request
         * @param response
         * @param type
         * @param paramIndex
         * @param method
         * @return
         */
        public Object argumentResolver(HttpServletRequest request,
                    HttpServletResponse response, Class<?> type, 
                    int paramIndex,
                    Method method);
    }

    处理Request请求参数

    import javax.servlet.ServletRequest;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import com.springframework.annotation.MyService;
    
    /*
     * 处理Request请求参数
     */
    @MyService("httpServletRequestArgumentResolver")
    public class HttpServletRequestArgumentResolver implements ArgumentResolver {
         
        @Override
        public boolean support(Class<?> type, int paramIndex, Method method) {
            return ServletRequest.class.isAssignableFrom(type);
        }
     
        @Override
        public Object argumentResolver(HttpServletRequest request,
                HttpServletResponse response, Class<?> type, int paramIndex,
                Method method) {
            return request;
        }
     
    }

    处理Response参数

    import java.lang.reflect.Method;
    
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import com.springframework.annotation.MyService;
    /*
     * 处理Response参数
     */
    @MyService("httpServletResponseArgumentResolver")
    public class HttpServletResponseArgumentResolver implements ArgumentResolver {
     
        @Override
        public boolean support(Class<?> type, int paramIndex, Method method) {
            return ServletResponse.class.isAssignableFrom(type);
        }
     
        @Override
        public Object argumentResolver(HttpServletRequest request,
                HttpServletResponse response, Class<?> type, int paramIndex,
                Method method) {
            return response;
        }
     
    }

    处理自定义的参数

    import java.lang.annotation.Annotation;
    import java.lang.reflect.Method;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import com.springframework.annotation.MyRequestParam;
    import com.springframework.annotation.MyService;
    
    @MyService("requestParamArgumentResolver")
    public class RequestParamArgumentResolver implements ArgumentResolver {
     
        @Override
        public boolean support(Class<?> type, int paramIndex, Method method) {
            // type = class java.lang.String
            // @MyRequestParam("name")String name
            //获取当前方法的参数
            Annotation[][] an = method.getParameterAnnotations();
            Annotation[] paramAns = an[paramIndex];
            
            for (Annotation paramAn : paramAns) {
                //判断传进的paramAn.getClass()是不是 MyRequestParam 类型
                if (MyRequestParam.class.isAssignableFrom(paramAn.getClass())) {
                    return true;
                }
            }
            
            return false;
        }
     
        @Override
        public Object argumentResolver(HttpServletRequest request,
                HttpServletResponse response, Class<?> type, int paramIndex,
                Method method) {
            
            //获取当前方法的参数
            Annotation[][] an = method.getParameterAnnotations();
            Annotation[] paramAns = an[paramIndex];
            
            for (Annotation paramAn : paramAns) {
                //判断传进的paramAn.getClass()是不是 MyRequestParam 类型
                if (MyRequestParam.class.isAssignableFrom(paramAn.getClass())) {
                    MyRequestParam cr = (MyRequestParam) paramAn;
                    String value = cr.value();
                    return request.getParameter(value);
                }
            }
            return null;
        }
     
    }

    启动服务,在URL输入请求地址:

    http://localhost:8080/myspringmvc/demo/user?name=xiaojiesir123&id=1

     

    总结:

    手写的spring和springmvc主要用了反射、注解。正式的spring中还有很多设计模式,值得我们学习。所以我们在掌握基础知识后,多读框架的源码,获取框架中的设计模式,设计理念更助于提升自己。

  • 相关阅读:
    noi 2011 noi嘉年华 动态规划
    最小乘积生成树
    noi 2009 二叉查找树 动态规划
    noi 2010 超级钢琴 划分树
    noi 2011 阿狸的打字机 AC自动机
    noi 2009 变换序列 贪心
    poj 3659 Cell Phone Network 动态规划
    noi 2010 航空管制 贪心
    IDEA14下配置SVN
    在SpringMVC框架下建立Web项目时web.xml到底该写些什么呢?
  • 原文地址:https://www.cnblogs.com/xiaojiesir/p/11157235.html
Copyright © 2011-2022 走看看