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