Spring MVC基本概念
顾名思义,需要了解m、v、c三个部分的概念,是熟悉和分析springmvc处理过程的首要条件。
M部分
M指的是model,也就是模型。在springmvc中的职责就是在后端进行页面的最后渲染时提供页面所需要的数据,这里所说的数据不仅仅是后端返回的业务数据,还包括模板引擎中的内置变量和工具类可以总结如下。
- spring框架中的model(业务数据)
- Model
- ModelAndView中的Model
- ModelMap
- JSP模板引擎
- JSP内置的九大对象
Thymeleaf模板引擎
- strings
- numbers
- context上下文
……
不同的模板引擎肯能内置不同变量,但是基本上model涉及的概念都是和以上的类似。
V部分
V部分指的是view,不能简单理解为html页面或者是jsp,应该是springmvc中,对view进行处理的一个完整的处理链 ,包括代表视图的视图对象,视图处理渲染等涉及对象。可以总结如下。
一个视图抽象为后端的View实例对象,来进行处理。view对象具有渲染页面功能。
- spring MVC中:InternalResourceView 和 RedirectView,一个表示转发请求一个表示重定向请求
- Thymeleaf中:ThymeleafView
构建抽象的View的对象
- jsp中:InternalResourceViewResolver
- Thymeleaf中:ThymeleafViewResolver
V部分处理过程在下面源码分析处讨论。
C部分
这是简单的部分,C指的是总控制器,就是我们熟悉的DispatcherServlet,所有的请求处理都需要经过这里。
依据源码分析springMVC工作过程
以Thymeleaf模板的mvc过程分析为例,基本上处理大同小异,基于spring-webmvc4.3.2源码进行分析。
在了解spring mvc对于请求处理的过程,需要先了解一些基本的请求处理过程中使用到的类以及其职能。
*HandlerMapping:主要负责处理映射request请求对应的HandlerMethod或者Handler。
处理器(HandlerMethod和Object[Handler]):HandlerMethod一个聚合对象,主要属性包括被@Controller注解修饰的实例bean和这个bean内@RequestMapping修饰的方法对象,最终会通过反射方式调用这个方法;Object[Handler]是一个bean的name,最终会获取到这个bean然后利用这个bean做相应操作。这里处理器都是伴随对应的HandlerMapping一起的,在对应HandlerMapping中进行初始化,一个*HandlerMapping有多个处理器,*HandlerMapping通过请求的url映射为对应处理器做相应操作。
处理器集合例如以下的
- org.springframework.web.servlet.handler.AbstractUrlHandlerMapping#handlerMap
- org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#mappingRegistry
*HandlerInterceptor:在通过反射执行HandlerMethod的方法之前,需要执行我们或者系统定义的HandlerInterceptor集合中每个HandlerInterceptor需要执行的org.springframework.web.servlet.HandlerInterceptor#preHandle方法;在反射调用执行结束时需要调用每个HandlerInterceptor的org.springframework.web.servlet.HandlerInterceptor#postHandle方法;
HandlerExecutionChain:拦截器(HandlerInterceptor)调用链持有对象,同时也持有处理方法(HandlerMethod),主要用于调度反射前调用HandlerInterceptor集合的org.springframework.web.servlet.HandlerInterceptor#preHandle和反射执行方法后的org.springframework.web.servlet.HandlerInterceptor#postHandle方法的调用。
*View:视图对象,有渲染最终视图的功能,不同的模板引擎有不同的View,例如ThymeleafView,InternalResourceView
*ViewResolver:视图对象构建器,同样对应不同模板引擎会有对应实现,例如InternalResourceViewResolver,ThymeleafViewResolver。
下面看源码分析。
DispatcherServlet
DispatcherServlet是spring mvc的总控制器,从接受请求到视图渲染的调度工作统一负责。请求处理的源码入口就是doDispatch方法。这里也是整个spring mvc处理过程的骨架方法。摘取部分重要代码显示如下。
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 {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
//1.映射request对应的处理器(HandlerMethod或者Handler),并且包装到HandlerExecutionChain中
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
//2.获取最佳匹配的HandlerInterceptor
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 (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//3.执行调用反射前HandlerInterceptor集合中每个元素的preHandle
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
//4.反射执行处理
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//5.设置viewName
applyDefaultViewName(processedRequest, mv);
//6.执行调用反射后HandlerInterceptor集合中每个元素的postHandle
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
//......
//7.渲染视图view,并输出
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
//......
}
}
下面针对骨架方法,逐个步骤分析。
1.映射request对应的处理器(HandlerMethod或者Handler),并且包装到HandlerExecutionChain中
先看这个方法的基本代码,org.springframework.web.servlet.DispatcherServlet#getHandler
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
基本就是在DispatcherServlet的HandlerMapping列表中选择一个最佳匹配的HandlerMapping来映射url对应的处理器,这个处理器可能是HandlerMethod,也可能是Handler。获得处理器之后把处理器包装进HandlerExecutionChain。 那么由以下几个问题。
1. 怎么决定使用哪个HandlerMapping?
2. HandlerMapping怎么映射为HandlerMethod或者Handler?
1.1怎么决定使用哪个HandlerMapping
首先查看DispatcherServlet中持有的HandlerMapping列表到底是如何实例化的?查找发现是在org.springframework.web.servlet.DispatcherServlet#initHandlerMappings这个方法里面进行初始化的,关键代码如下。
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
}
}
大致含义就是在context中查找到所有的HandlerMapping.class的bean,然后添加到DispatchServlet中的HandlerMapping列表中。这个过程是在DispatcherServlet初始化时完成。
然后怎么决定使用哪一个HandlerMapping?其实每一个都可能,在org.springframework.web.servlet.DispatcherServlet#getHandler中可以看到其实是循环方式调用AbstractHandlerMapping#getHandler方法尝试匹配一个处理器,如果匹配出来就进行返回的,所以其实每个HandlerMapping都可能调用一下。
默认情况这个HandlerMapping列表有以下几个HandlerMapping:
在SpringMVC通常都是RequestMappingHandlerMapping来映射调用Controller的@RequestMapping修饰的方法。以下面的一个请求为例。
@Controller
public class ThymeleafController {
@RequestMapping("/")
public String indexPage(){
return "index";
}
}
请求这个链接时RequestMappingHandlerMapping就能匹配出一个处理器来。至于怎么匹配出来的?请看下面。
1.2. HandlerMapping怎么映射为HandlerMethod或者Handler?
在上面知道是循环调用HandlerMapping的getHandler方法来进行匹配处理器的,进一步跟踪其实是调用org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler方法,代码如下。
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//映射出HandlerMethod或者Handler?
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
//构造HandlerExecutionChain
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
所以关键方法是getHandlerInternal,跟踪发现有以下两个实现,分别对应返回Handler和HandlerMethod。
- org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal
- org.springframework.web.servlet.handler.AbstractUrlHandlerMapping#getHandlerInternal
上面两个对应的继承子类分别是。
- RequestMappingHandlerMapping
- SimpleUrlHandlerMapping
正好对应两种不同映射处理器结果,大多数是这两种情况。
先看映射出HandlerMethod的org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal方法,代码如下。
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
//获得request请求路径
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
//......
this.mappingRegistry.acquireReadLock();
try {
//根据请求路径映射HandlerMethod
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
//......
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
//......
}
继续看lookupHandlerMethod,代码如下。
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<Match>();
//根据request请求url匹配对应的处理器
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
// No choice but to go through all mappings...
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}
if (!matches.isEmpty()) {
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
Collections.sort(matches, comparator);
//......
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
if (CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
//匹配多个,报错
throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
}
}
handleMatch(bestMatch.mapping, lookupPath, request);
//返回最佳匹配
return bestMatch.handlerMethod;
}
//......
}
上面的主要过程就是根据request的请求url去mappingRegistry
中获取匹配的HandlerMethod,类似于key-val形式去匹配,最后返回经过判断获取最佳的匹配返回。
同样地,其实org.springframework.web.servlet.handler.AbstractUrlHandlerMapping#getHandlerInternal返回Handler的匹配方式也是一样的,不过这里的Handler就是一个Bean或者bean的StringName。
现在有个问题就是mappingRegistry
里面维护的HandlerMethod或者Handler都是怎么初始化的?或者说是从哪里来的?
1.3. mappingRegistry
的初始化(Handlermethod和Handler)
可以先跟踪HandlerMethod的mappingRegistry初始化过程。
直接在AcstrackMethodHandlerMapping搜索看有没有初始化代码,有,在org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#register中,代码如下。
public void register(T mapping, Object handler, Method method) {
this.readWriteLock.writeLock().lock();
try {
//利用handler和method构建一个HandlerMethod对象
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
assertUniqueMethodMapping(handlerMethod, mapping);
if (logger.isInfoEnabled()) {
logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
}
this.mappingLookup.put(mapping, handlerMethod);
List<String> directUrls = getDirectUrls(mapping);
for (String url : directUrls) {
this.urlLookup.add(url, mapping);
}
String name = null;
if (getNamingStrategy() != null) {
name = getNamingStrategy().getName(handlerMethod, mapping);
addMappingName(name, handlerMethod);
}
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
this.corsLookup.put(handlerMethod, corsConfig);
}
//利用传入的mapping作为key存储Handlermethod
this.registry.put(mapping, new MappingRegistration<T>(mapping, handlerMethod, directUrls, name));
}
finally {
this.readWriteLock.writeLock().unlock();
}
}
可以看到这里利用handler和method构建一个HandlerMethod对象,然后利用传入的mapping作为key存储Handlermethod。我们可以推测传入的mapping是基于@RequestMapping配置的url构造的;而handler则是Controller对应的bean;method则是对应需要调用的方法。为了验证推测还需要继续往上层调用链跟踪代码。
- AbstractHandlerMethodMapping.MappingRegistry#register
- AbstractHandlerMethodMapping#registerHandlerMethod
- AbstractHandlerMethodMapping#detectHandlerMethods
- AbstractHandlerMethodMapping#initHandlerMethods
- AbstractHandlerMethodMapping#afterPropertiesSet
来到detectHandlerMethods,代码如下。
protected void detectHandlerMethods(final Object handler) {
//handler推测是Controller对象实例
Class<?> handlerType = (handler instanceof String ?
getApplicationContext().getType((String) handler) : handler.getClass());
final Class<?> userType = ClassUtils.getUserClass(handlerType);
//获取被@RequestMapping修饰的方法
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
new MethodIntrospector.MetadataLookup<T>() {
@Override
public T inspect(Method method) {
try {
return getMappingForMethod(method, userType);
}
catch (Throwable ex) {
throw new IllegalStateException("Invalid mapping on handler class [" +
userType.getName() + "]: " + method, ex);
}
}
});
if (logger.isDebugEnabled()) {
logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
}
//遍历Method对象,注册初始化HandlerMethod
for (Map.Entry<Method, T> entry : methods.entrySet()) {
Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
T mapping = entry.getValue();
registerHandlerMethod(handler, invocableMethod, mapping);
}
}
再往上initHandlerMethods,代码如下。
protected void initHandlerMethods() {
if (logger.isDebugEnabled()) {
logger.debug("Looking for request mappings in application context: " + getApplicationContext());
}
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
//取出上下文所有的bean
for (String beanName : beanNames) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
Class<?> beanType = null;
try {
beanType = getApplicationContext().getType(beanName);
}
catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
if (logger.isDebugEnabled()) {
logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
}
}
//判断bean是不是handler
if (beanType != null && isHandler(beanType)) {
detectHandlerMethods(beanName);
}
}
}
handlerMethodsInitialized(getHandlerMethods());
}
主要逻辑就和注释写的一样,所以我们只需要再看isHandler方法逻辑就可以知道到底哪些bean会注册HandlerMethod,isHandler代码如下。
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
根据上面的代码结果很明显,就是Controller注解修饰或者RequestMapping修饰的bean就会进行HandlerMethod注册的过程。符合我们了解的点。
最后可以看到整个HandlerMethod初始化的过程起点是AbstractHandlerMethodMapping#afterPropertiesSet,也就是熟知在bean初始化完成后调用的方法里面开始执行HandlerMethod初始化过程。
这里完全可以用相同的方式跟踪Handler处理器的注册过程,基本大同小异。Handler判断是否需要注册到Map的逻辑是这个Handler在spring 容器里面name属性是否是以”/”开始的,如果是,那么就是可注册,以key-val形式存储,key同样是请求url,val是beanName,请求到达时就可以获取到这个bean实例作为Handler处理器,当然还需要搭配对应的HandlerAdapter才能正常工作。
1.4. 构造HandlerExecutionChain
获得处理器HandlerMethod之后就要构造一个HandlerExecutionChain对象返回给DispatcherServlet了,代码如下。
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}
非常简单,就是把DispatchServlet持有的HandlerInterceptor加入到HandlerExecutionChain持有的HandlerInterceptor集合里面就行了。那么这里的DispatcherServlet持有的HandlerInterceptor集合是哪里来的?跟踪调用处,可以看到初始化代码如下。
protected void initApplicationContext() throws BeansException {
extendInterceptors(this.interceptors);
detectMappedInterceptors(this.adaptedInterceptors);
initInterceptors();
}
protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
mappedInterceptors.addAll(
BeanFactoryUtils.beansOfTypeIncludingAncestors(
getApplicationContext(), MappedInterceptor.class, true, false).values());
}
基本上就是获得了MappedInterceptor以及MappedInterceptor实现的接口的HandlerInterceptor所有的实现类bean加入到列表中。
2.获取最佳匹配的HandlerInterceptor
如何获取映射获得的处理器的最佳HandlerInterceptor?先看代码如下。
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
for (HandlerAdapter ha : this.handlerAdapters) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
}
//判断是否支持该适配器
if (ha.supports(handler)) {
return ha;
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
跟HandlerMapping一样,遍历DispatcherServlet#handlerAdapters,看哪个合适就用哪个,但是这里唯一不一样的是HandlerInterceptor的实现类都实现了Ordered接口,也就是说DispatcherServlet#handlerAdapters想必是有序的,分优先级。可以看一下DispatcherServlet#handlerAdapters初始化代码。
private void initHandlerAdapters(ApplicationContext context) {
this.handlerAdapters = null;
if (this.detectAllHandlerAdapters) {
// Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
//找出上下文中所有实现HandlerAdapter接口的类的bean然后加入到DispatcherServlet#handlerAdapters列表中
Map<String, HandlerAdapter> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerAdapters = new ArrayList<HandlerAdapter>(matchingBeans.values());
// We keep HandlerAdapters in sorted order.
//但是,需要经过依次排序
AnnotationAwareOrderComparator.sort(this.handlerAdapters);
}
}
else {
try {
HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
this.handlerAdapters = Collections.singletonList(ha);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerAdapter later.
}
}
//.......
}
abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered
所以结论是,ordered优先级高的且适配的HandlerAdapter优先使用。
然后看一下怎么判断是否适配的?org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#supports的实现如下
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
接着是org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#supportsInternal的实现如下。
protected boolean supportsInternal(HandlerMethod handlerMethod) {
return true;
}
所以RequestMappingHandlerAdapter适用于所有处理器是HandlerMethod的情况。同样可以判断处理器是Handler的情况是使用哪个适配器。或者也许我们可以扩展自定义的适配器。
3.执行调用反射前HandlerInterceptor集合中每个元素的preHandle
这一步也是非常简单,代码如下。
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}
值得注意的是这里的编码方式并不是意义上的责任链模式,这里的链式调用依赖外部的循环调用,同时终止是由元素的返回值决定。
4.反射执行处理
这里的反射执行处理指的是处理器是Handlermethod类型,适配器是RequestMappingHandlerAdapter的。代码如下。AbstractHandlerMethodAdapter#handle->RequestMappingHandlerAdapter#handleInternal,
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
checkRequest(request);
// Execute invokeHandlerMethod in synchronized block if required.
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
//反射调用方法,返回ModelAndView,具体不细看
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;
}
5.设置viewName
private void applyDefaultViewName(HttpServletRequest request, ModelAndView mv) throws Exception {
if (mv != null && !mv.hasView()) {
mv.setViewName(getDefaultViewName(request));
}
}
protected String getDefaultViewName(HttpServletRequest request) throws Exception {
return this.viewNameTranslator.getViewName(request);
}
public String org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator#getViewName(HttpServletRequest request) {
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
//前后缀拼接视图名称,后续用于寻址模板资源(html)
return (this.prefix + transformPath(lookupPath) + this.suffix);
}
6.执行调用反射后HandlerInterceptor集合中每个元素的postHandle
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(request, response, this.handler, mv);
}
}
}
7.渲染视图view,并输出
- DispatcherServlet#processDispatchResult
- DispatcherServlet#render
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine locale for request and apply it to the response.
Locale locale = this.localeResolver.resolveLocale(request);
response.setLocale(locale);
View view;
if (mv.isReference()) {
// 1.调用ViewResolver#resolveViewName 创建一个新的View对象
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
if (view == null) {
//........ex
}
}
else {
// No need to lookup: the ModelAndView object contains the actual View object.
view = mv.getView();
if (view == null) {
//........ex
}
//......
try {
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
//2.利用新产生的view对象渲染视图
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
//........
throw ex;
}
}
7.1.调用ViewResolver#resolveViewName 创建一个新的View对象
上面是结果视图渲染的基本骨架,进一步先看resolveViewName方法,代码如下。
protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,
HttpServletRequest request) throws Exception {
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
return null;
}
这里DispatcherServlet#viewResolvers的初始化方式跟HandlerInterceptor一模一样,同样根据Ordered排序,viewResolvers是查找所有实现ViewResolver的类的bean加入到列表。然后是具体的ViewResolver调用resolveViewName。
下面这个图的使用Thymeleaf模板时默认的viewResolvers,可以看到优先级最高的是一个叫做ContentNegotiatingViewResolver
经过测试,ContentNegotiatingViewResolver#resolveViewName里面实际上也是遍历ContentNegotiatingViewResolver里面持有的ViewResolver#resolveViewName,所有能够适配的ViewResolver都能创建一个view,然后ContentNegotiatingViewResolver选出最佳适配。ContentNegotiatingViewResolver持有是ViewResolver如下所示。主要代码如下。
public View resolveViewName(String viewName, Locale locale) throws Exception {
RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
Assert.isInstanceOf(ServletRequestAttributes.class, attrs);
List<MediaType> requestedMediaTypes = getMediaTypes(((ServletRequestAttributes) attrs).getRequest());
if (requestedMediaTypes != null) {
//遍历ViewResolver获取候选View
List<View> candidateViews = getCandidateViews(viewName, locale, requestedMediaTypes);
//筛选最佳匹配View
View bestView = getBestView(candidateViews, requestedMediaTypes, attrs);
if (bestView != null) {
return bestView;
}
}
if (this.useNotAcceptableStatusCode) {
if (logger.isDebugEnabled()) {
logger.debug("No acceptable view found; returning 406 (Not Acceptable) status code");
}
return NOT_ACCEPTABLE_VIEW;
}
else {
logger.debug("No acceptable view found; returning null");
return null;
}
}
所以说其实如果在ContentNegotiatingViewResolver中都找不到适配的View的话,其实在后面再跑一次viewResolver.resolveViewName(viewName, locale)也是没意义的。
然后就是假如我们的模板引擎是Thymeleaf,那么就会自动配置一个ThymeleafViewResolver,它的resolveViewName方法是来自AbstractCachingViewResolver#resolveViewName。这里的大概逻辑是如果View对应Viewname和locale有缓存对象那么就读取缓存,没有就新建一个ViewObject。
- AbstractCachingViewResolver#resolveViewName
- AbstractCachingViewResolver#createView
- ThymeleafViewResolver#createView(看项目具体使用的模板引擎)
- ThymeleafViewResolver#loadView(看项目具体使用的模板引擎)
假如使用Thymeleaf,那么就是使用ThymeleafViewResolver#loadView新建一个ThymeleafView.class的实例。假如是jsp,那么就是使用InternalResourceViewResolver#loadView新建一个View,然后经过ContentNegotiatingViewResolver进行赛选匹配最佳的View。匹配方式下次在分析。
7.2.利用新产生的view对象渲染视图
这里假设使用的是Thymeleaf模板,那么调用的是ThymeleafView#render方法,跟踪代码调用链如下。
ThymeleafView#render
ThymeleafView#renderFragment(主要准备渲染页面所需Model)
TemplateEngine#process(String, IContext,IFragmentSpec, Writer)
TemplateEngine#process(String, IProcessingContext, IFragmentSpec, Writer)
TemplateEngine#process(TemplateProcessingParameters, IFragmentSpec, Writer)
最终TemplateEngine#process(TemplateProcessingParameters, IFragmentSpec, Writer)代码如下
private void process(final TemplateProcessingParameters templateProcessingParameters,
final IFragmentSpec fragmentSpec, final Writer writer) {
final String templateName = templateProcessingParameters.getTemplateName();
//使用ResourceResolver,设置templateResolution中模板文件Resource路径
final Template template = this.templateRepository.getTemplate(templateProcessingParameters);
final TemplateResolution templateResolution = template.getTemplateResolution();
final String templateMode = templateResolution.getTemplateMode();
Document document = template.getDocument();
//......
final Arguments arguments =
new Arguments(this,
templateProcessingParameters, templateResolution,
this.templateRepository, document);
if (document != null) {
document.process(arguments);
}
if (logger.isDebugEnabled()) {
logger.debug("[THYMELEAF][{}] Finished process on template \"{}\" using mode \"{}\"",
new Object[] { TemplateEngine.threadIndex(), templateName, templateMode });
}
final ITemplateModeHandler templateModeHandler =
this.configuration.getTemplateModeHandler(templateMode);
final ITemplateWriter templateWriter = templateModeHandler.getTemplateWriter();
if (templateWriter == null) {
}
try {
// It depends on the ITemplateWriter implementation to allow nulls or not.
// Standard writer will simply not write anything for null.
//渲染模板字符
templateWriter.write(arguments, writer, document);
} catch (IOException e) {
throw new TemplateOutputException("Error during creation of output", e);
}
/*
* Finally, flush the writer in order to make sure that everything has been written to output
*/
try {
//输出渲染完成的字符
writer.flush();
} catch (final IOException e) {
throw new TemplateOutputException("An error happened while flushing output writer", e);
}
}
可以看writer.flush();输出结果如下图。