SpringMVC是java常用的Web框架,采用MVC设计模型。本文梳理SpringMVC处理请求的流程和对应的主线源码,以及不同Controller的实现。
1 处理请求的流程
1.1 图解处理流程
在看源码前,首先应该要胸有成竹,先画个图,对SpringMVC处理请求的整体流程有个大概的认识。
- 客户端向服务器发送HTTP请求;
- 服务器的
前端控制器
拿到请求后,根据请求URL的路径到处理器映射器
查询对应的处理器执行链
(包含了:拦截器
和处理器
); - 然后根据
处理器
去匹配对应的处理器适配器
; - 由
处理器适配器
来调用处理器执行链
里的处理器
,去执行业务逻辑; 处理器
会返回ModelAndView
对象(包含了视图名称和数据);前端控制器
找视图解析器
去解析视图;- 最后是渲染视图,返回响应数据给客户端。
1.2 结合源码学习
1.2.1 创建工程
首先创建一个SpringMVC的maven工程,be like:
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模式下,我们可以看到以下栈信息:
通过栈信息上面的包名,我们可以很容易区别出来,由下到上是:
- 首先是Tomcat服务器包的调用;
- 其次到javax-servlet-api的调用;
- 然后再到SpringMVC的调用;
- 最后调到了我们写的这个DemoController类上面。
===> SpringMVC是基于servlet的,只要Web服务器支持servlet就可以部署SpringMVC框架的应用。
我们现在要学习的是SpringMVC的源码,所以应该从这个FrameworkServlet
的doGet
方法开始:
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 小结
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注解,也支持简单类型的形参。
小结一下流程:
- 获取方法上的形参数组,去遍历解析入参;
- 解析参数时,先判断是否有对应的参数解析器支持解析;
- 再调用对应的参数解析器去解析参数。
注意:本文归作者所有,未经作者允许,不得转载