引言
作者采用的是springMVC xml的方式进行的学习。
springMVC请求映射支持多种注解类型,当你在类上或方法上使用一个注解就可以达到相应的功能,这不得不让人惊叹这其中的神奇!在写这篇文章时,我参考了很多博客,通过idea一调试就是一天,希望能通过我自己的理解能给你带来帮助,本人能力水平有限,若有错误之处,还请指出。
请求流程
自己画的,有点小丑,但问题不大,先有个请求流程概念,再阅读源码
1.首先用户通过浏览器请求到达了springMVC DispatchServlet核心类(底层是通过servlet调用)
2.通过request对象查找合适的处理器
3.拿到处理器后获得controller对象 根据该对象去寻找合适的处理适配器
4.然后进行数据封装一系列业务逻辑
5.封装完成后拿到封装的对象数组 进行反射调用controller方法
6.执行完controller方法后返回modelAndView (数据和视图)
7.再对根据视图解析得到对应的页面 比如是jsp还是thymeleaf
8.最后得到页面返回给浏览器
策略对象初始化
因为我们从前端请求过来的需求有多种多样,那么后台该如何应对多样业务或数据进行区分呢?
springMVC提供了一个业务接口和多种实现类策略对象这种方式,用来适用对应的业务,进行相应的业务逻辑处理。
springMVC通过遍历所有的策略对象来适配策略,只要按顺序满足其中一种策略便结束查找,若没有查找到则抛出异常 (图不是很标准,大家了解大概意思)
在启动服务器时会调用initStrategies方法,初始化策略对象。
public class DispatcherServlet extends FrameworkServlet {
/**
//初始化这个servlet使用的策略对象。
* Initialize the strategy objects that this servlet uses.
* <p>May be overridden in subclasses in order to initialize further strategy objects.
*/
//用来对后面请求操作提供策略对象
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
//初始化映射策略对象
initHandlerMappings(context);
//初始化controller对象适配器策略对象
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
//初始化视图解析策略对象
initViewResolvers(context);
initFlashMapManager(context);
}
}
核心doDispatch方法
该方法是DispatchServlet中的重要核心方法,他将协调一次请求的所有业务逻辑,后续的知识点将对这个方法进行细分,可以先对这个方法执行了那些业务做一个概括的了解。
public class DispatcherServlet extends FrameworkServlet {
/**
* 获得相应的策略进行相应的业务逻辑处理
* 按顺序来获得适配的Handler处理器
* HandlerAdapter将通过处理器对象适配获得
* 只能获得一个
* 所有的http方法都将被这个方法处理,由handleradapter或handlers本身决定哪些方法是可适配的。
* @param request current HTTP request
* @param response current HTTP response
* @throws Exception in case of any kind of processing failure
*/
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 {
//如果提交的是Multipart请求方式 内部采取对应的方式进行解析
//若不是 request不变
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 将request传递 来获取对应的处理器(请求适配)
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
// 将处理器中的handler对象传递进去 获取到对应的适配器(对象适配)
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;
}
}
//执行拦截器PreHandle方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
//PreHandle方法要是返回false 内部依旧会执行拦截器的afterCompletion方法
//最终结束请求
return;
}
// Actually invoke the handler.
//底层通过反射去调用controller的方法 然后将数据和视图返回给mv
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//如果没有定义视图名将采用默认的视图名
applyDefaultViewName(processedRequest, mv);
//执行拦截器的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);
}
///视图解析方法
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
//执行拦截器中的afterCompletion方法
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
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);
}
}
}
}
}
接下来,我们来对doDispatch这个方法执行的业务进行解剖
1.获取执行链对象
mappedHandler = getHandler(processedRequest);
通过request对象去获取适合本身的处理器
public class DispatcherServlet extends FrameworkServlet {
/** List of HandlerMappings used by this servlet. */
//在服务器启动时已经进行初始化
@Nullable
private List<HandlerMapping> handlerMappings;
/**
* Return the HandlerExecutionChain for this request.
* <p>Tries all handler mappings in order. 按顺序尝试所有处理程序映射
* @param request current HTTP request
* @return the HandlerExecutionChain, or {@code null} if no handler could be found
*/
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
//遍历集合中所有HandlerMapping的实现类(策略对象)
for (HandlerMapping mapping : this.handlerMappings) {
//通过request对象来适配合适的处理器
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
//只要有一个策略满足就返回
return handler;
}
}
}
return null;
}
}
拿到的HandlerExecutionChain 对象,我们看注释,可以知道这个类是由我们当前的处理器对象和多个拦截器组成的,包括我们自定义的拦截器都会添加到这个对象中
/**
//处理器执行链,由对象和和多个拦截器组成
* Handler execution chain, consisting of handler object and any handler interceptors.
* Returned by HandlerMapping's {@link HandlerMapping#getHandler} method.
*/
public class HandlerExecutionChain {
//在构造方法中被赋值为controller对象
private final Object handler;
//拦截器对象
@Nullable
private HandlerInterceptor[] interceptors;
@Nullable
private List<HandlerInterceptor> interceptorList;
//会调用拦截器preHandle方法
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception
//会调用拦截器postHandle方法
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
//会调用拦截器afterCompletion方法
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
.....省略
}
2.获取适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
通过我们前面拿到的HandlerExecutionChain对象,将handler对象传递进行,获取到对应的策略处理适配器
public class DispatcherServlet extends FrameworkServlet {
/**
* Return the HandlerAdapter for this handler object.
* @param handler the handler object to find an adapter for
* @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
*/
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");
}
}
3.数据封装
在了解数据封装之前,我们得知道这个接口,所有的数据解析封装策略都是基于该接口去实现的,我们可以看到有两个方法,我们简单了解一下
public interface HandlerMethodArgumentResolver {
//true该策略支持该参数
boolean supportsParameter(MethodParameter var1);
//当supportsParameter方法返回true 便会执行这个方法进行数据解析封装
@Nullable
Object resolveArgument(MethodParameter var1, @Nullable ModelAndViewContainer var2, NativeWebRequest var3, @Nullable WebDataBinderFactory var4) throws Exception;
}
下面大多是数据解析的策略对象,都是通过实现HandlerMethodArgumentResolver接口,我们也可以自己实现接口来自定义一个数据解析的策略对象
回过头来,我们再来看我们的源代码。
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
在找到适配器后,执行适配器的handle方法直到调用InvocableHandlerMethod类的invokeForRequest方法进行数据封装,最终通过反射调用controller的方法并拿到数据和视图赋值给mv
public class InvocableHandlerMethod extends HandlerMethod {
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
//getMethodArgumentValues方法进行数据封装
Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs);
if (this.logger.isTraceEnabled()) {
this.logger.trace("Arguments: " + Arrays.toString(args));
}
//内部通过反射调用controller方法 并将封装好的参数传递过去
return this.doInvoke(args);
}
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
//获得controller方法入参个数
MethodParameter[] parameters = this.getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {
//如果为空直接返回空数组
return EMPTY_ARGS;
} else {
//创建对应参数长度的数组
Object[] args = new Object[parameters.length];
for(int i = 0; i < parameters.length; ++i) {
MethodParameter parameter = parameters[i]
if (args[i] == null) {
if (!this.resolvers.supportsParameter(parameter)) {
//没有找到该参数的解析器时抛出异常
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
//通过调用HandlerMethodArgumentResolver接口实现类的resolveArgument方法()寻找支持该参数类型的解析器进行数据封装
//并将参数传递进去
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
} catch (Exception var10) {
throw var10;
}
}
}
//封装完后,返回封装数据
return args;
}
}
}
HandlerMethodArgumentResolverComposite 类是数据封装协调的核心类,采用了设计模式的组合模式,对不同的参数注解 比如我们常用的@RequestParam @ReuqestBody等 采取不同的执行策略
//寻找合适的数据解析器核心类
public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {
//用来存放默认数据解析类
private final List<HandlerMethodArgumentResolver> argumentResolvers = new LinkedList();
//存放已经解析过的参数,加快查找过程
private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache = new ConcurrentHashMap(256);
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
//getArgumentResolver()方法传递参数来获得适合的解析器
HandlerMethodArgumentResolver resolver = this.getArgumentResolver(parameter);
if (resolver == null) {
//找不到的话抛出异常
throw new IllegalArgumentException("Unsupported parameter type [" + parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
} else {
//然后调用当前获取到的解析器的resolveArgument()方法来进行数据解析封装处理
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
}
@Nullable
//查找对应的参数解析器
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
//先从集合读取对应的解析器,如果上一次读取过则可以直接读取到
HandlerMethodArgumentResolver result = (HandlerMethodArgumentResolver)this.argumentResolverCache.get(parameter);
if (result == null) {
//没有在集合中找到对应的解析器
//迭代所有的解析器
Iterator var3 = this.argumentResolvers.iterator();
while(var3.hasNext()) {
HandlerMethodArgumentResolver resolver = (HandlerMethodArgumentResolver)var3.next();
if (resolver.supportsParameter(parameter)) {
//找到支持的数据解析器
result = resolver;
//添加到集合中,方便下次查找,直接取出
this.argumentResolverCache.put(parameter, resolver);
break;
}
}
}
//没有查到找则返回空
return result;
}
}
我们来对支持@ReuqestParam 注解的策略supportsParameter方法简单讲解一下,方便大家了解
public class RequestParamMapMethodArgumentResolver implements HandlerMethodArgumentResolver {
public boolean supportsParameter(MethodParameter parameter) {
//可以简单的看出 只要参数上注解了@RequestParam 便会返回true 表示支持该类型
RequestParam requestParam = (RequestParam)parameter.getParameterAnnotation(RequestParam.class);
return requestParam != null && Map.class.isAssignableFrom(parameter.getParameterType()) && !StringUtils.hasText(requestParam.name());
}
....省略业务逻辑方法
}
4.反射调用
前面已经对数据解析和封装的执行原理进行了讲解,数据正确封装后将通过反射调用controller方法
public class InvocableHandlerMethod extends HandlerMethod {
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
//getMethodArgumentValues方法进行数据封装
Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs);
if (this.logger.isTraceEnabled()) {
this.logger.trace("Arguments: " + Arrays.toString(args));
}
//内部通过反射调用controller方法 并将封装好的参数传递过去
return this.doInvoke(args);
}
}
5.ModelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
反射调用完后,会将view和model返回给mv;
6.视图解析
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
视图解析同样也有着不同的处理策略,比如是jsp的视图解析还是thymeleaf的视图解析。
public class DispatcherServlet extends FrameworkServlet {
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
//有异常则向前端返回错误页面
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
//异常解析
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// 视图不为空的情况下
if (mv != null && !mv.wasCleared()) {
//视图解析方法
render(mv, request, response);
if (errorView) {
//清理异常错误信息
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("No view rendering, null ModelAndView returned.");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
if (mappedHandler != null) {
//执行拦截的AfterCompletion方法
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
}
视图解析方法,方便理解,只保留了本内容重要代码
public class DispatcherServlet extends FrameworkServlet {
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
View view;
String viewName = mv.getViewName();
if (viewName != null) {
//根据视图名 采取不同策略进行解析
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() + "'");
}
}
try {
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
//视图渲染 看下一标题内容
view.render(mv.getModelInternal(), request, response);
}
}
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) {
//如果你在xml中配置了视图解析器 这时就会调用他进行视图解析
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
//解析完成返回view
return view;
}
}
}
return null;
}
}
获得相应的视图解析策略 博主使用的是jsp页面 这里获取的是自定义实现的internalResourceView对象
进行的视图解析
实现ViewResolver 接口的大概策略,博主没有对策略具体源码进行过研究,就不带大家去探讨了,有兴趣的小伙伴可以去了解
7.视图渲染
获取到解析的view对象后 准备进行视图渲染转发 因为这个InternalResourceView类没有重写父类的render方法所以这里使用的是父类AbstractView类的render方法
public abstract class AbstractView extends WebApplicationObjectSupport implements View, BeanNameAware {
public void render(@Nullable Map<String, ?> model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
//将model中的数据写入到mergedModel集合中
Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
//当通过HTTPS发送下载内容时,默认实现为IE漏洞提供了一个解决方案。
prepareResponse(request, response);
//页面转发
renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}
}
public class InternalResourceView extends AbstractUrlBasedView {
/**
* Render the internal resource given the specified model.
* This includes setting the model as request attributes.
*/
@Override
protected void renderMergedOutputModel(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
// model中的数据写入到request域中
exposeModelAsRequestAttributes(model, request);
// Expose helpers as request attributes, if any.
exposeHelpers(request);
// 获得转发路径
String dispatcherPath = prepareForRendering(request, response);
// 获取目标资源(通常是JSP)的RequestDispatcher。
RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
if (rd == null) {
throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
"]: Check that the corresponding file exists within your web application archive!");
}
//判断是否是include
if (useInclude(request, response)) {
response.setContentType(getContentType());
if (logger.isDebugEnabled()) {
logger.debug("Including [" + getUrl() + "]");
}
rd.include(request, response);
}
else {
// Note: The forwarded resource is supposed to determine the content type itself.
if (logger.isDebugEnabled()) {
logger.debug("Forwarding to [" + getUrl() + "]");
}
//请求转发 视图渲染
rd.forward(request, response);
}
}
}
总结
sprinMVC的请求核心类是DispatchServlet,其中的doDispatch是核心方法
在一次请求中
- 首先会对request请求方式进行解析
- 然后通过request对象获得的映射处理器
- 通过处理器获得handler对象,根据对象获得对象适配器
- 然后执行拦截器的prehandle方法 如果返回false 执行拦截器afterCompletion方法 结束请求
- 若返回true再对 则执行适配器的hanle方法
- 首先查找参数适配策略进行数据封装成一个对象数组
- 封装完毕后通过反射将对象数组一齐传递调用controller方法
- 然后回调到doDispatch方法 将controller方法中返回的视图名和数据封装给modelview
- 下一步执行拦截器的posthanle方法
- 执行后 对modelview选择对应策略进行视图解析
- 获得解析后的view对象 将model数据写入request域中 进行页面渲染
- 执行拦截器afterCompletion方法后 最后响应页面
spring的思想博主了解的还是只是皮毛,本篇文章可能存在误解,还请大家指出,一起进步。
天赋不够,汗水来凑,加油!