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中还有很多设计模式,值得我们学习。所以我们在掌握基础知识后,多读框架的源码,获取框架中的设计模式,设计理念更助于提升自己。

  • 相关阅读:
    MQTT介绍与使用
    SVN的搭建与使用
    Git版本控制之ubuntu搭建Git服务器
    蓝奏云的速度好快
    放大器的定义和主要参数
    模拟信号导论
    模拟电子电路学习笔记
    二极管单向导电的理解
    让蜂鸣器发声
    蜂鸣器的介绍
  • 原文地址:https://www.cnblogs.com/xiaojiesir/p/11157235.html
Copyright © 2011-2022 走看看