SpringMVC源码学习1

ragnar 1年前 ⋅ 968 阅读

SpringMVC是java常用的Web框架,采用MVC设计模型。本文梳理SpringMVC处理请求的流程和对应的主线源码,以及不同Controller的实现。

1 处理请求的流程

1.1 图解处理流程

在看源码前,首先应该要胸有成竹,先画个图,对SpringMVC处理请求的整体流程有个大概的认识。

SpringMVC处理请求的流程.png

  • 客户端向服务器发送HTTP请求;
  • 服务器的前端控制器拿到请求后,根据请求URL的路径到处理器映射器查询对应的处理器执行链(包含了:拦截器处理器);
  • 然后根据处理器去匹配对应的处理器适配器
  • 处理器适配器来调用处理器执行链里的处理器,去执行业务逻辑;
  • 处理器会返回ModelAndView对象(包含了视图名称和数据);
  • 前端控制器视图解析器去解析视图;
  • 最后是渲染视图,返回响应数据给客户端。

1.2 结合源码学习

1.2.1 创建工程

首先创建一个SpringMVC的maven工程,be like:

搭建maven工程.png

pom.xml引入下面的依赖:

<!-- spring上下文 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.8.RELEASE</version>
</dependency>
<!-- SpringMVC -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.8.RELEASE</version>
</dependency>
<!-- Servlet -->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>servlet-api</artifactId>
    <version>2.5</version>
    <scope>provided</scope>
</dependency>
<!-- JSP -->
<dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>jsp-api</artifactId>
    <version>2.0</version>
    <scope>provided</scope>
</dependency>

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>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 配置读取springmvc.xml -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <!-- 配置初始参数,启动应用时创建Servlet对象 -->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <!-- Servlet映射 -->
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

DemoController类:

public class DemoController implements Controller {

    @Override
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("success");
        String name = httpServletRequest.getParameter("name");
        if (name != null) {
            ModelMap modelMap = modelAndView.getModelMap();
            modelMap.put("name", name);
        }
        return modelAndView;
    }
}

再弄一个简单的jsp页面:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>成功页面</title>
</head>
<body>
执行成功!
</body>
</html>

1.2.2 整体处理流程

首先我们写DemoController里打个断点,在Debug模式下,我们可以看到以下栈信息:

SpringMVC调Controller的栈信息.png

通过栈信息上面的包名,我们可以很容易区别出来,由下到上是:

  • 首先是Tomcat服务器包的调用;
  • 其次到javax-servlet-api的调用;
  • 然后再到SpringMVC的调用;
  • 最后调到了我们写的这个DemoController类上面。

===> SpringMVC是基于servlet的,只要Web服务器支持servlet就可以部署SpringMVC框架的应用。

我们现在要学习的是SpringMVC的源码,所以应该从这个FrameworkServletdoGet方法开始:

public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
    // 。。。
    
    @Override
    protected final void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    /**
     * Process this request, publishing an event regardless of the outcome.
     * The actual event handling is performed by the abstract doService template method.
     * 处理此请求,无论结果如何,都会发布事件。实际事件处理由抽象的 doService 模板方法执行。
     */
    protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        // 。。。
        try {
            doService(request, response);
        }
        catch (ServletException | IOException ex) {}
        catch (Throwable ex) {}
        // 。。。
    }
    
}

由上可以看到doGet方法都没做逻辑处理,那就直接调到processRequest方法了。 这里就调到了分发请求的实现类DispatcherServlet,也就是我们上面提到的前端控制器,其中的doDispatch方法就是SpringMVC处理请求的核心:

public class DispatcherServlet extends FrameworkServlet {
    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // ...

        try {
            // 分发请求
            this.doDispatch(request, response);
        } finally {}
    }

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            try {
                ModelAndView mv = null;
                Exception dispatchException = null;

                try {
                    processedRequest = this.checkMultipart(request);
                    multipartRequestParsed = processedRequest != request;
                    // 根据请求URL的路径到“处理器映射器”查询对应的“处理器执行链”
                    mappedHandler = this.getHandler(processedRequest);
                    if (mappedHandler == null) {
                        noHandlerFound(processedRequest, response);
                        return;
                    }

                    // 根据“处理器”去匹配对应的“处理器适配器”
                    HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                    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;
                        }
                    }
                    
                    // 拦截器--前置处理
                    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                        return;
                    }

                    // “处理器适配器”去调用“处理器”
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }

                    applyDefaultViewName(processedRequest, mv);
                    // 拦截器--后置处理
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                }
                catch (Exception ex) {}
                catch (Throwable err) {}

                // 处理结果
                processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
            } 		
            catch (Exception ex) {}
            catch (Throwable err) {}

        } finally {}
    }

    // 处理结果
    private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception {
        // 。。。

        if (mv != null && !mv.wasCleared()) {
            // 解析&渲染视图
            this.render(mv, request, response);
            if (errorView) {}
        } else if (this.logger.isTraceEnabled()) {}
        // 。。。
    }

    /**
     * Render the given ModelAndView.
     * <p>This is the last stage in handling a request. It may involve resolving the view by name.
     * 呈现给定的 ModelAndView。这是处理请求的最后一个阶段。它可能涉及按名称解析视图。
     */
    protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 。。。
        View view;
        String viewName = mv.getViewName();
        if (viewName != null) {
            // We need to resolve the view name.
            // 我们需要解析视图名称。
            view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
            if (view == null) {
                throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
                        "' in servlet with name '" + getServletName() + "'");
            }
        }
        else {
            // 。。。
        }

        // Delegate to the View object for rendering.
        // 委托给 View 对象进行渲染。
        try {
            // 。。。
            // 渲染视图
            view.render(mv.getModelInternal(), request, response);
        }
        catch (Exception ex) {
            // 。。。
            throw ex;
        }
    }

    /**
     * Resolve the given view name into a View object (to be rendered).
     * <p>The default implementations asks all ViewResolvers of this dispatcher.
     * Can be overridden for custom resolution strategies, potentially based on
     * specific model attributes or request parameters.
     * 将给定的视图名称解析为一个 View 对象(要呈现)。
     * 默认实现会询问此调度程序的所有 ViewResolver。可以覆盖自定义解析策略,可能基于特定的模型属性或请求参数。
     */
    protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
                                   Locale locale, HttpServletRequest request) throws Exception {

        if (this.viewResolvers != null) {
            for (ViewResolver viewResolver : this.viewResolvers) {
                // 根据视图名称,去解析视图
                View view = viewResolver.resolveViewName(viewName, locale);
                if (view != null) {
                    return view;
                }
            }
        }
        return null;
    }
}

处理器适配器是怎么去调用处理器的呢?由于我们的DemoController实现了Controller接口,只需要强转一下类型,就可以直接调用了。

public class SimpleControllerHandlerAdapter implements HandlerAdapter {
    @Override
    @Nullable
    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        // 调用处理器(也就是我们写的Controller)
        return ((Controller) handler).handleRequest(request, response);
    }
}

2 不同Controller的实现

实现Controller有几种方式:

  • @Controller注解方式(适配器:RequestMappingHandlerAdapter);
  • 实现Controller接口(适配器:SimpleControllerHandlerAdapter);
  • 实现HttpRequestHandler接口(适配器:HttpRequestHandlerAdapter);
  • 实现Servlet接口(适配器:SimpleServletHandlerAdapter);

2.1 处理器映射器

  • RequestMappingHandlerMapping ===> 负责:@Controller注解方式的
  • BeanNameUrlHandlerMapping ===> 负责:实现Controller接口的、实现HttpRequestHandler接口的、实现Servlet接口的
  • SimpleUrlHandlerMapping ===> 静态资源

2.2 处理器适配器

所有的Contrller的适配器,都实现了HandlerAdapter接口。

其中SimpleControllerHandlerAdapter / HttpRequestHandlerAdapter / SimpleServletHandlerAdapter在调用处理器时, 都是简单的强转处理器的类型,由Object强转为对应的处理器类型。比如实现Controller接口的:

public class SimpleControllerHandlerAdapter implements HandlerAdapter {
    @Override
    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return ((Controller) handler).handleRequest(request, response);
    }
}

而注解方式的的处理器适配器RequestMappingHandlerAdapter较为复杂,通过其父类AbstractHandlerMethodAdapter间接实现了HandlerAdapter接口:

public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {
    /**
     * This implementation expects the handler to be an {@link HandlerMethod}.
     */
    public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {

        return handleInternal(request, response, (HandlerMethod) handler);
    }
}
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
        implements BeanFactoryAware, InitializingBean {
    @Override
    protected ModelAndView handleInternal(HttpServletRequest request,
                                          HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
        ModelAndView mav;
        checkRequest(request);

        // Execute invokeHandlerMethod in synchronized block if required.
        // 执行invokeHandlerMethod方法 
        if (this.synchronizeOnSession) {
            HttpSession session = request.getSession(false);
            if (session != null) {
                Object mutex = WebUtils.getSessionMutex(session);
                synchronized (mutex) {
                    mav = invokeHandlerMethod(request, response, handlerMethod);
                }
            }
            else {
                // No HttpSession available -> no mutex necessary
                mav = invokeHandlerMethod(request, response, handlerMethod);
            }
        }
        else {
            // No synchronization on session demanded at all...
            mav = invokeHandlerMethod(request, response, handlerMethod);
        }

        if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
            if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
                applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
            }
            else {
                prepareResponse(response);
            }
        }

        return mav;
    }

    /**
     * Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView}
     * if view resolution is required.
     * 调用处理器方法(也就是我们定义的@RequestMapping注解的方法)
     */
    protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
                                               HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
        ServletWebRequest webRequest = new ServletWebRequest(request, response);
        try {
            WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
            ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

            // ServletInvocableHandlerMethod 是 HandlerMethod 的子类,
            // 使用了装饰者模式,目的是做增强,处理返回值
            ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
            // ...

            ModelAndViewContainer mavContainer = new ModelAndViewContainer();
            // ...

            // 调用
            invocableMethod.invokeAndHandle(webRequest, mavContainer);
            if (asyncManager.isConcurrentHandlingStarted()) {
                return null;
            }

            // 获取 ModelAndView对象
            return getModelAndView(mavContainer, modelFactory, webRequest);
        }
        finally {
            webRequest.requestCompleted();
        }
    }
}

ServletInvocableHandlerMethod是InvocableHandlerMethod的子类,调用其invokeAndHandle方法后,会调用到InvocableHandlerMethod的invokeForRequest方法。

public class InvocableHandlerMethod extends HandlerMethod {
    public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
                                   Object... providedArgs) throws Exception {
        // 从请求中获取入参
        Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
        if (logger.isTraceEnabled()) {
            logger.trace("Arguments: " + Arrays.toString(args));
        }
        // 真正调用
        return doInvoke(args);
    }

    @Nullable
    protected Object doInvoke(Object... args) throws Exception {
        ReflectionUtils.makeAccessible(getBridgedMethod());
        try {
            // 通过反射调用我们定义的@RequestMapping方法
            return getBridgedMethod().invoke(getBean(), args);
        }
        catch (IllegalArgumentException ex) {
            // ...
        }
        catch (InvocationTargetException ex) {
            // ...
        }
    }
}

为什么可以这样通过反射调用?请看下InvocableHandlerMethod的父类HandlerMethod,其中就包含了我们@RequestMapping方法所在的bean对象,以及其Method对象。

public class HandlerMethod {
    // ...
    
    // @Controller的bean对象
    private final Object bean;

    @Nullable
    private final BeanFactory beanFactory;

    // @Controller的bean对象的Class类型
    private final Class<?> beanType;

    // @RequestMapping标识的方法
    private final Method method;

    // 桥接方法
    private final Method bridgedMethod;

    // @RequestMapping标识的方法的形参
    private final MethodParameter[] parameters;
    
    // ...
}

2.3 小结

不同Controller实现的差异.png

3 参数注入

参数注入也就是上面注解方式实现的Controller在处理器适配器调用处理器时,去获取请求参数的这一步骤。

public class InvocableHandlerMethod extends HandlerMethod {

    // 参数解析器(HandlerMethodArgumentResolvers)的组合
    private HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite();

    // 参数名称发现者
    private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
    
    // 。。。
    
    // 从请求中获取入参
    protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
                                               Object... providedArgs) throws Exception {
        // 获取@RequestMapping方法上的形参数组
        MethodParameter[] parameters = getMethodParameters();
        if (ObjectUtils.isEmpty(parameters)) {
            return EMPTY_ARGS;
        }

        // 遍历数组解析入参
        Object[] args = new Object[parameters.length];
        for (int i = 0; i < parameters.length; i++) {
            MethodParameter parameter = parameters[i];
            parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
            // providedArgs 当前场景为空
            args[i] = findProvidedArgument(parameter, providedArgs);
            if (args[i] != null) {
                continue;
            }
            // 解析器组合先判断能否支持当前的形参
            if (!this.resolvers.supportsParameter(parameter)) {
                throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
            }
            try {
                // 去解析器组合去解析入参
                args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
            }
            catch (Exception ex) {
                // 。。。
            }
        }
        return args;
    }
}

由上面可以看到HandlerMethodArgumentResolverComposite是解析参数的主线。

public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {
    // 参数解析器集合
    private final List<HandlerMethodArgumentResolver> argumentResolvers = new LinkedList<>();
    
    // 。。。

    // 解析参数
    public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
        // 获取参数解析器
        HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
        if (resolver == null) {
            throw new IllegalArgumentException("Unsupported parameter type [" +
                    parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
        }
        // 调用参数解析器去解析参数
        return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
    }
    // 。。。
}

参数注入的类型主要的还是形参上面的注解,比如:@RequestParam、@PathVariable、@RequestBody等都有专门的参数解析器。 此外,还有一些特殊类型的形参,会有专门的参数解析器去处理,比如:HttpServletRequest。 最后就是那没有加注解的普通类型的形参,则是根据名称注入。

使用频率较多的应该是RequestParamMethodArgumentResolver参数解析器,支持@RequestParam注解,也支持简单类型的形参。

小结一下流程:

  • 获取方法上的形参数组,去遍历解析入参;
  • 解析参数时,先判断是否有对应的参数解析器支持解析;
  • 再调用对应的参数解析器去解析参数。

全部评论: 0

    我有话说:

    目录