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

原文地址:https://www.cnblogs.com/xiaojiesir/p/11157235.html