通过前面 SpringAOP源码深度解析 , SpringIOC源码深度解析 加上本文的SpringMVC的源码阅读,我从中收获很多,学习了各种设计模式,各种抽象思想,以及各种底层原理,比如动态代理,反射等等,虽然前前前后后大概花了一个多月,但是我不后悔,并不觉得是浪费时间。
本文比较长,我花了三天的时间完成本文,但是还是水平有限,不可能面面俱到,当中也可能会有错的,还请读者指出,一起交流一起进步。
本文采用的源码版本是5.2.x,同样,为了能收获更多,还请读者打开Spring的源码工程进行跟进。
为什么要先了解Servlet的知识呢,因为后面你会看到我们所熟悉的SpringMVC其实也是一个Servlet,只是它封装了很多的东西并和Spring进行了整合,后面我们进行的源码分析就是围绕着Servlet的生命周期进行的,所以有必要了解一下Servlet相关的知识。
全称 Java Servlet ,是用Java编写的服务器端程序。其主要功能在于 交互式地浏览和修改数据,生成动态Web内容。Servlet运行于支持 Java应用的服务器中。从原理上讲,Servlet可以响应任何类型的请求, 但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。
下面通过一张时序图来理解Servlet的工作流程
从上面的时序图总结如下:
打开Servlet源码发现Servlet接口有几个方法:
public interface Servlet { //Servlet创建时会调用init方法进行初始化 void init(ServletConfig var1) throws ServletException; ServletConfig getServletConfig(); // 每次有新的请求来时都会调用 void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException; String getServletInfo(); // 请求结束时调用 void destroy(); } 复制代码
写一个AddUserServlet类继承自HttpServlet(为什么要继承这个类后面有说明)
public class AddUserServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/plain;charset=utf8"); response.getWriter().write("添加成功"); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } } 复制代码
webapp/WEB-INF下新建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_2_5.xsd" version="2.5"> <servlet> <description></description> <display-name>AddUserServlet</display-name> <servlet-name>AddUserServlet</servlet-name> <servlet-class>com.sjc.springmvc.servlet.AddUserServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>AddUserServlet</servlet-name> <url-pattern>/AddUserServlet</url-pattern> </servlet-mapping> </web-app> 复制代码
将程序部署到Tomcat就可以访问了(具体不会的请读者查相关资料)
有同学可能会产生疑惑:我都没有看到main方法,怎么就可以访问了呢?
Servlet实际上是tomcat容器生成的,调用init方法可以初始化。他有别于 普通java的执行过程,普通java需要main方法;但是web项目由于是服务器控 制的创建和销毁,所以servlet的访问也需要tomcat来控制。通过tomcat访问 servlet的机制是通过使用http协议的URL来访问,所以servlet的配置完想要访 问,必须通过URL来访问,所以没有main方法。
也就是我们常说的web层
MVC是模型(model)、视图(view)、控制器(controller)的缩写,是一种用于设计编写web应用程序表现层的模式
MVC设计模式的三大角色:
Model(模型):
模型包含业务模型和数据模型,数据模型用于封装数据,业务模型用于处理业务。
View(视图): 通常指的是我们的jsp或者html。作用一般就是展示数据的。
通常视图是依据数据模型创建的。
Controller(控制器):
是应用程序中处理用户交互的部分。作用一般就是处理程序逻辑的。
理解SpringMVC,只要你理解了下面介绍的六大组件基本上就可以了。后面的源码分析我们也是围绕着这六大组件来的。
SpringMVC流程如下图所示:
总结如下:
DispatcherServlet: 前端控制器
用户请求到达前端控制器,它就相当于MVC中的C,DispatcherServlet是整个流程控制的中心,由它调用其他组件处理用户的请求,DispatcherServlet的存在降低了组件之间的耦合性
Handler是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理
由于Handler涉及到具体的用户业务请求,所以一般情况下需要程序员根据业务需求开发Handler
常用的有比如Controller,HttpRequestHandler,Servlet、@RequestMapping等等,所以说处理器其实是一个宽泛的概念
SpringMVC框架提供了很多的view视图类型的支持,包括:jstlview、freemarkerview、pdfview等。我们最常见的视图就是jsp。 当然,现在很少用jsp了,现在大部分都是前后端分离了,所以后面源码分析我们会忽略视图
HandlerMapping负责根据用户请求找到Handler即处理器,SpringMVC提供了不同的映射器实现不同的映射方式比如:配置文件方式(BeanNameUrlHandlerMapping)、实现接口方式和注解方式(RequestMappingHandlerMapping)等。
SpringMVC通过适配器模式,将不关联的 DispatcherServlet 和Handler进行关联,通过适配器调用具体的Handler实现。
View Resolver负责将处理结果生成view视图,View Resolver首先根据逻辑视图解析成物理视图名即具体的页面地址,再生成view视图对象,最后对view进行渲染,将处理结果通过页面展示给用户。
我们上面说过,分析源码的时候从Servlet入手,我们看它的初始化init()。首先看下类结构图,我们发现DispatcherServlet这个核心组件就是一个Servlet,回到开头我们说的SpringMVC其实也是一个Servlet,只是做的事情比较多而已。
我们顺着这个类关系图,找到了FrameworkServlet#initServletBean
这里初始化了spring容器WebApplicationContext
@Override protected final void initServletBean() throws ServletException { //...省略若干代码 // 初始化web环境中的spring容器WebApplicationContext this.webApplicationContext = initWebApplicationContext(); initFrameworkServlet(); //...省略若干代码 } 复制代码
我们进入到:FrameworkServlet#initWebApplicationContext
我们找到了两个分支
这个分支会去初始化Spring容器,又会回到我们SpringIOC容器初始化的那十二步骤,相关的可以阅读我之前分析的 深度解析SpringIOC
会刷新容器的策略,我们主要看这一分支
protected WebApplicationContext initWebApplicationContext() { //...省略若干代码 // 初始化spring容器 configureAndRefreshWebApplicationContext(cwac); // 刷新容器中的策略 onRefresh(wac); //...省略若干代码 } 复制代码
我们根据onRefresh,发现最终会进入到DispatcherServlet#onRefresh
protected void onRefresh(ApplicationContext context) { // 初始化策略容器 initStrategies(context); } 复制代码
我们进入到DispatcherServlet#initStrategies
这里会初始各种解析器,比如我们比较关心的处理器映射器,处理器适配器,至于为啥要在这里做初始化呢? SpringMVC为了扩展性,使用策略模式,将这些映射器适配器交给了配置文件,这样如果要再新增一个处理器就不需要改代码了,符合“对修改关闭,对扩展开放”的设计原则,这里的初始化也是为了策略模式做准备 这个也是我们学习源码学习到的知识,以后可以运用到我们实际的项目中。
比如下面的xml配置文件:
initStrategies就是从SpringIOC容器中获取到这些Bean,然后放入Map中来进行初始化的。
<beans> <!-- Handler处理器类的配置 --> <!-- 通过bean标签,建立beanname和bean的映射关系 --> <bean name="/queryUser2" class="com.sjc.springmvc.handler.QueryUserHandler"></bean> <bean name="/addUser2" class="com.sjc.springmvc.handler.AddUserHandler"></bean> <!-- HandlerMapping配置 --> <bean class="com.sjc.springmvc.handlermapping.BeanNameUrlHandlerMapping" init-method="init"></bean> <!-- HandlerAdapter配置 --> <bean class="com.sjc.springmvc.handleradapter.HttpRequestHandlerAdapter"></bean> </beans> 复制代码
读者感兴趣的话可以当做为一个分支进行验证。
protected void initStrategies(ApplicationContext context) { // 初始化多部件解析器 initMultipartResolver(context); // 初始化国际化解析器 initLocaleResolver(context); // 初始化主题解析器 initThemeResolver(context); // 初始化处理器映射器 initHandlerMappings(context); // 初始化处理器适配器 initHandlerAdapters(context); // 初始化异常解析器 initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); // 初始会视图解析器 initViewResolvers(context); initFlashMapManager(context); } 复制代码
在介绍HandlerMapping找到Handler的过程前,我们先来看看,RequestMappingHandlerMapping的初始化过程发生了什么。我这里先给个结论:
我们进入到RequestMappingHandlerMapping,看到其中只有这样的方法:
@Override public void afterPropertiesSet() { this.config = new RequestMappingInfo.BuilderConfiguration(); this.config.setUrlPathHelper(getUrlPathHelper()); this.config.setPathMatcher(getPathMatcher()); this.config.setSuffixPatternMatch(this.useSuffixPatternMatch); this.config.setTrailingSlashMatch(this.useTrailingSlashMatch); this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch); this.config.setContentNegotiationManager(getContentNegotiationManager()); // 调用父类AbstractHandlerMethodMapping的afterPropertiesSet方法 super.afterPropertiesSet(); } 复制代码
我们眼前一亮,它为我们提供了研究RequestMappingHandlerMapping初始化的入口,为什么这么说呢?我们知道SpringIOC提供了两种初始化方式: 第一种、就是在配置文件中中指定init-method方法,这种在我前面分析SpringIOC的文章可以看到**深入解析SpringIOC; **第二种、就是子类实现InitializingBean接口。这个接口有一个方法:
public interface InitializingBean { void afterPropertiesSet() throws Exception; } 复制代码
这样在初始化Bean的时候会调用afterPropertiesSet()方法。
这个流程我们在SpringIOC源码哪里可以看见呢?
我们进入AbstractAutowireCapableBeanFactory#invokeInitMethods
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd) throws Throwable { //判断该bean是否实现了实现了InitializingBean接口,如果实现了InitializingBean接口,则只掉调用bean的afterPropertiesSet方法 boolean isInitializingBean = (bean instanceof InitializingBean); if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) { if (logger.isTraceEnabled()) { logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'"); } if (System.getSecurityManager() != null) { try { AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> { //直接调用afterPropertiesSet ((InitializingBean) bean).afterPropertiesSet(); return null; }, getAccessControlContext()); } catch (PrivilegedActionException pae) { throw pae.getException(); } } else { //直接调用afterPropertiesSet ((InitializingBean) bean).afterPropertiesSet(); } } if (mbd != null && bean.getClass() != NullBean.class) { String initMethodName = mbd.getInitMethodName(); //判断是否指定了init-method方法,如果指定了init-method方法,则再调用制定的init-method if (StringUtils.hasLength(initMethodName) && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) && !mbd.isExternallyManagedInitMethod(initMethodName)) { //进一步查看该方法的源码,可以发现init-method方法中指定的方法是通过反射实现 invokeCustomInitMethod(beanName, bean, mbd); } } } 复制代码
这两种方式哪一种先调用呢?
哪一种方式的效率高呢?
好了别跑远了,我们接着看RequestMappingHandlerMapping#afterPropertiesSet
里面会调用父类AbstractHandlerMethodMapping#afterPropertiesSet
public void afterPropertiesSet() { // 初始化处理器方法对象 initHandlerMethods(); } 复制代码
接着进入:AbstractHandlerMethodMapping#initHandlerMethods
protected void initHandlerMethods() { // 获取当前spring容器的所有bean的name,并遍历 for (String beanName : getCandidateBeanNames()) { if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) { // 处理候选的Bean processCandidateBean(beanName); } } handlerMethodsInitialized(getHandlerMethods()); } 复制代码
我们关心的是processCandidateBean
进入:AbstractHandlerMethodMapping#processCandidateBean
这里面主要做三件事:
protected void processCandidateBean(String beanName) { Class<?> beanType = null; try { // 根据bean的名称,从当前spring容器中获取对应的Bean的Type beanType = obtainApplicationContext().getType(beanName); } catch (Throwable ex) { // An unresolvable bean type, probably from a lazy bean - let's ignore it. if (logger.isTraceEnabled()) { logger.trace("Could not resolve type for bean '" + beanName + "'", ex); } } // 如果是Handler,则需要查找HandlerMethod(如果带有@Controller或者@RequestMapping则是Handler对象) if (beanType != null && isHandler(beanType)) { // 重要入口 // 从Controller或者RequestMapping注解的Bean中,找到所有的HandlerMethod对象,并进行存储 detectHandlerMethods(beanName); } } 复制代码
我们进入AbstractHandlerMethodMapping#detectHandlerMethods
这个方法比较复杂,主要用的lambda表达式太多,主要做这几件事:
protected void detectHandlerMethods(Object handler) { // 获取处理器类型 Class<?> handlerType = (handler instanceof String ? obtainApplicationContext().getType((String) handler) : handler.getClass()); if (handlerType != null) { // 如果该类是通过cglib代理的代理类,则获取其父类类型,否则的话,直接返回该类 Class<?> userType = ClassUtils.getUserClass(handlerType); // 存储Method方法和RequestMapping注解信息的映射关系(重点) // 该映射关系会解析成我们需要的其他两个映射关系 // key是Controller类中的Method对象,value是RequestMappingInfo对象 Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (MethodIntrospector.MetadataLookup<T>) method -> { // 此处是设置回调函数 try { // 获取bean上面和method上面的RequestMapping注解信息,封装到RequestMappingInfo对象中 return getMappingForMethod(method, userType); } catch (Throwable ex) { throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, ex); } }); if (logger.isTraceEnabled()) { logger.trace(formatMappings(userType, methods)); } methods.forEach((method, mapping) -> { Method invocableMethod = AopUtils.selectInvocableMethod(method, userType); // 注册HandlerMethod和RequestMappingInfo对象的关系 // 注册请求URL和RequestMappingInfo对象的关系 registerHandlerMethod(handler, invocableMethod, mapping); }); } } 复制代码
RequestMappingHandlerMapping初始化分析到此结束,在深入下去就会没完没了。
同样顺着下面的流程图我们找到实现Servlet#service()方法的类HttpServlet
我们来到:HttpServlet#service:
可以看到这里面将ServletRequest转成HttpServletRequest, ServletResponse转成HttpServletResponse,这样就可以做更多的事情了。
@Override public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { HttpServletRequest request; HttpServletResponse response; if (!(req instanceof HttpServletRequest && res instanceof HttpServletResponse)) { throw new ServletException("non-HTTP request or response"); } request = (HttpServletRequest) req; response = (HttpServletResponse) res; service(request, response); } 复制代码
进入到HttpServlet#service(request, response):
所有的Http请求都会经过这里,但是我们找半天没发现service实现代码在哪里,我们看到HttpServlet是一个抽象类,一般类被设计成抽象类有两个因素:
那这个HttpServlet为什么要设计成抽象类呢?别急,我们看下类的注释文档:
翻译起来大概的意思就是我这个类不知道你子类是什么处理请求的,我不会帮你处理的,我这里定义好了各种请求,请你务必实现其中的某一个,不然我就给你返回错误。我们看到这里就是用了 抽象模板方法的设计模式:父类把其他的逻辑处理完,把不确定的业务逻辑抽象成一个抽象方法,交给子类去实现。
/** * * Provides an abstract class to be subclassed to create * an HTTP servlet suitable for a Web site. A subclass of * <code>HttpServlet</code> must override at least * one method, usually one of these: * * <ul> * <li> <code>doGet</code>, if the servlet supports HTTP GET requests * <li> <code>doPost</code>, for HTTP POST requests * <li> <code>doPut</code>, for HTTP PUT requests * <li> <code>doDelete</code>, for HTTP DELETE requests * <li> <code>init</code> and <code>destroy</code>, * to manage resources that are held for the life of the servlet * <li> <code>getServletInfo</code>, which the servlet uses to * provide information about itself * </ul> * / 复制代码
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod(); if (method.equals(METHOD_GET)) { long lastModified = getLastModified(req); if (lastModified == -1) { // servlet doesn't support if-modified-since, no reason // to go through further expensive logic doGet(req, resp); } else { long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE); if (ifModifiedSince < lastModified) { // If the servlet mod time is later, call doGet() // Round down to the nearest second for a proper compare // A ifModifiedSince of -1 will always be less maybeSetLastModified(resp, lastModified); doGet(req, resp); } else { resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED); } } } else if (method.equals(METHOD_HEAD)) { long lastModified = getLastModified(req); maybeSetLastModified(resp, lastModified); doHead(req, resp); } else if (method.equals(METHOD_POST)) { doPost(req, resp); } else if (method.equals(METHOD_PUT)) { doPut(req, resp); } else if (method.equals(METHOD_DELETE)) { doDelete(req, resp); } else if (method.equals(METHOD_OPTIONS)) { doOptions(req,resp); } else if (method.equals(METHOD_TRACE)) { doTrace(req,resp); } else { // // Note that this means NO servlet supports whatever // method was requested, anywhere on this server. // String errMsg = lStrings.getString("http.method_not_implemented"); Object[] errArgs = new Object[1]; errArgs[0] = method; errMsg = MessageFormat.format(errMsg, errArgs); resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg); } } 复制代码
我们顺藤摸瓜,找到实现HttpServlet的子类,看看哪个子类实现了service()方法,我们最终看到了DispatcherServlet实现了这个service()方法。
这里我们千呼万唤的doDispatch终于出来了,这个doDispatch做了它擅长的事情,就是 请求的分发 ,我们得慢慢品,细细品这个方法。
@Override protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { //...省略掉无数代码 // 处理请求分发(做调度) doDispatch(request, response); } 复制代码
我们进入到:DispatcherServlet#doDispatch
我们再回顾一下SpringMVC的处理流程:
这个方法就是干这件事情的,一张图胜似千言万语。 你品,你细品
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { ModelAndView mv = null; Exception dispatchException = null; try { // 处理文件上传的request请求 processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); // Determine handler for the current request. // 通过处理器映射器HandlerMapping,获取handler处理器执行链,该执行链封装了处理器和对应该处理器的拦截器(可能有多个) // 需要注意的是@Controller注解的类,它不是我们这里要查找的处理器,我们要查找的处理器是@RequestMapping对应的方法,这个方法会封装到HandlerMethod类中 mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; } // Determine handler adapter for the current request. // 通过找到的handler处理器,去匹配合适的处理器适配器HandlerAdapter HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Process last-modified header, if supported by the handler. String method = request.getMethod(); boolean isGet = "GET".equals(method); if (isGet || "HEAD".equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } // 执行拦截器(interceptor)的preHandle方法 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // Actually invoke the handler. // 通过处理器适配器,真正调用处理器方法 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } // 设置默认视图名称 applyDefaultViewName(processedRequest, mv); // 执行拦截器(interceptor)的postHandle方法 mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } catch (Throwable err) { // As of 4.3, we're processing Errors thrown from handler methods as well, // making them available for @ExceptionHandler methods and other scenarios. dispatchException = new NestedServletException("Handler dispatch failed", err); } // 处理调度结果(也就是ModelAndView对象) processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { // 执行拦截器(interceptor)的afterCompletion方法 triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Throwable err) { // 执行拦截器(interceptor)的afterCompletion方法 triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", err)); } finally { if (asyncManager.isConcurrentHandlingStarted()) { // Instead of postHandle and afterCompletion if (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else { // Clean up any resources used by a multipart request. if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } } } 复制代码
我们先来看下请求通过HandlerMapping找到Handler的过程:
我们进入到DispatcherServlet#getHandler
不出我们所料,这里就遍历了我们初始化阶段存储的handlerMappings集合,返回HandlerExecutionChain。这里使用到了策略模式:
@Nullable protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if (this.handlerMappings != null) { // 遍历所有的处理器映射器 for (HandlerMapping mapping : this.handlerMappings) { // 通过处理器映射器,查找具体的处理器执行链 HandlerExecutionChain handler = mapping.getHandler(request); if (handler != null) { return handler; } } } return null; } 复制代码
何为策略模式呢?我们一起来看:
我们假设有这样的需求:我们出行方式有很多种,比如火车,飞机,自行车,我们只需要输入我们有的钱就可以智能地匹配出我们的出行方式
我们来建立这样的模型:
定义一个策略类:
// 策略类 public interface TravelStrategy { //出行方式 void travelWay(); boolean isDone(int type); } 复制代码
定义飞机出行方式类:AirStrategy
public class AirStrategy implements TravelStrategy { @Override public void travelWay() { System.out.println("坐飞机"); } @Override public boolean isDone(int type) { if (type <= 1000 && type >500) { return true; } return false; } } 复制代码
定义自行车出行方式类: BicycleStrategy
public class BicycleStrategy implements TravelStrategy { @Override public void travelWay() { System.out.println("自行车"); } @Override public boolean isDone(int type) { if (type <= 20) { return true; } return false; } } 复制代码
定义火车出行方式类:TrainStrategy
public class TrainStrategy implements TravelStrategy { @Override public void travelWay() { System.out.println("坐火车"); } @Override public boolean isDone(int type) { if (type >= 20 && type < 400) { return true; } return false; } } 复制代码
定义一个策略模式环境类(Context)
public class PersonContext { // 策略类集合 private List<TravelStrategy> strategylist; public PersonContext() { this.strategylist = new ArrayList<>(); strategylist.add(new AirStrategy()); strategylist.add(new TrainStrategy()); strategylist.add(new BicycleStrategy()); } public void travel(int type) { // 输入一个数,循环遍历每个策略类,进行最优选择 for (TravelStrategy travelStrategy : strategylist) { if (travelStrategy.isOK(type)) { travelStrategy.travelWay(); break; } } } } 复制代码
测试类:
public class StrategyTest { @Test public void test() { // 策略环境类 PersonContext person = new PersonContext(); // 坐飞机 person.travel(1500); // 坐火车 person.travel(100); // 自行车 person.travel(1); } } 复制代码
输出:
坐飞机 坐火车 自行车 复制代码
我们再来看SpringMVC中的策略模式,首先环境类DispatcherServlet, 初始化各个策略模式的是DispatcherServlet#initStrategies, 遍历策略选择最合适的策略的是:
@Nullable protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if (this.handlerMappings != null) { // 遍历所有的处理器映射器 for (HandlerMapping mapping : this.handlerMappings) { // 通过处理器映射器,查找具体的处理器执行链 HandlerExecutionChain handler = mapping.getHandler(request); if (handler != null) { return handler; } } } return null; } 复制代码
策略模式消除了很多的if....else...代码,通过配置文件方式定义各种策略,是一种可扩展的设计模式。
我们进入到AbstractHandlerMapping#getHandler
主要做两件事:
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { // 调用具体的子类去获取不同类型的处理器对象(比如获取到的@Controller和@RequestMapping注解的处理器是HandlerMethod对象) Object handler = getHandlerInternal(request); //...省略若干代码 // 创建处理器执行链 HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); //...省略若干代码 return executionChain; } 复制代码
我们来到实现类AbstractHandlerMethodMapping#getHandlerInternal
我们看到这里的handler是HandlerMethod,这个类封装了controller和Method,
@Override protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { // 获取查找路径(部分URL) String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); request.setAttribute(LOOKUP_PATH, lookupPath); this.mappingRegistry.acquireReadLock(); try { // 根据请求查找路径(部分URL),获取最合适的HandlerMethod对象 HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); // 对HandlerMethod对象包含的的bean属性进行实例化,再返回HandlerMethod对象 return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); } finally { this.mappingRegistry.releaseReadLock(); } } 复制代码
我们进入到AbstractHandlerMethodMapping#lookupHandlerMethod
这里主要做:
根据请求路径(URL)去上面我们分析到的urlLookup集合中获取匹配到的RequestMappingInfo集合
将RequestMappingInfo对象和HandlerMethod对象进行匹配,将匹配到的信息封装到Match对象,再将Match对象放入matches集合进行返回。
此时我们的处理器HandlerMethod已经找到。
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { List<Match> matches = new ArrayList<>(); // 根据查找路径(URL)去urlLookup集合中获取匹配到的RequestMappingInfo集合 List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath); if (directPathMatches != null) { // 将RequestMappingInfo对象和HandlerMethod对象进行匹配,将匹配到的信息封装到Match对象,再将Match对象放入matches集合 addMatchingMappings(directPathMatches, matches, request); } if (matches.isEmpty()) { // No choice but to go through all mappings... addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request); } //...省略寻找最优匹配的过程代码 } 复制代码
我们再回到 DispatcherServlet#doService 方法中
找到Handler后按照我们上面的流程图,接下来就是要找到HandlerAdapter。
我们看到:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { // 通过找到的handler处理器,去匹配合适的处理器适配器HandlerAdapter HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); } 复制代码
我们进入到:DispatcherServlet#getHandlerAdapter
这里无非就是根据我们初始化过程中,将配置文件中的HandlerAdapters集合进行遍历,找到合适的HandlerAdapter。
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { if (this.handlerAdapters != null) { for (HandlerAdapter adapter : this.handlerAdapters) { // 通过适配器的适配功能,去适配处理器,如果适配成功,则直接将适配器返回 if (adapter.supports(handler)) { return adapter; } } } throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler"); } 复制代码
这里用到了适配器模式,我们来看看HandlerAdapter类:
public interface HandlerAdapter { // 判断是否与当前的适配器支持,如果支持返回true,不支持返回false boolean supports(Object handler); // 调用handler中的请求处理方法 @Nullable ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception; long getLastModified(HttpServletRequest request, Object handler); } 复制代码
SpringMVC这里为啥需要适配器呢?我们来分析一下,由于处理器handler有很多,比如Controller,HttpRequestHandler,Servlet等等,从HandlerMapping中获取到的Handler是一个Object对象,这样在DispatcherServlet#getHandlerAdapter方法中如果不用适配器模式就可能这样写:
private HandlerAdapter getHandlerAdapter(Object handler) { if (handler instanceof HttpRequestHandler) { return new HttpRequestHandlerAdapter(); } else if(handler instanceof Controller) { return new Controller } // else if..... return null; } 复制代码
如果再新增一个handler就得改代码,不符合“ 对修改关闭,对扩展开放 ”的设计原则。
SpringMVC这里就给每个handler对应一个相应的适配器,比如针对Controller实现的handler对应的是SimpleControllerHandlerAdapter适配器,由SimpleControllerHandlerAdapter适配器调用Controller处理器处理相关逻辑,每个handler的处理逻辑不一样,以后再新增一个handler的时候,只用新增相应的适配器就可以了,这完美的解决了这个问题。希望读者能细品其中的设计思想。
我们继续看DispatcherServlet中的其他方法:
找到HandlerAdapter后,主要做以下几件事:
SpringMVC的 拦截器 功能源码就在此步骤完成,如果读者对此感兴趣可以以此为分支进行深入研究,至此SpringMVC源码分析告一段落。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { //...省略若干代码 // 通过找到的handler处理器,去匹配合适的处理器适配器HandlerAdapter HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // 执行拦截器(interceptor)的preHandle方法 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // Actually invoke the handler. // 通过处理器适配器,真正调用处理器方法 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); // 设置默认视图名称 applyDefaultViewName(processedRequest, mv); // 执行拦截器(interceptor)的postHandle方法 mappedHandler.applyPostHandle(processedRequest, response, mv); // 处理调度结果(也就是ModelAndView对象) processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); // 执行拦截器(interceptor)的afterCompletion方法 triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } 复制代码