浅析Spring系列
Spring MVC是Spring系列框架中使用频率最高的部分,因此掌握Spring MVC的处理流程是很重要的,方便实现扩展和问题分析。本文将会简要分析SpringMVC处理的一个请求的流程,抓住主要脉络,了解相关组件,Model相关内容不是本文考虑的重点。Spring整合Tomcat的例子可以查看附录。
1. 概述
Spring提供了DispatcherServlet作为统一的请求入口分发,不论是doGet、doPost等请求,其处理逻辑主要在processRequest中。
其中会先初始化RequestAttributes,并且通过RequestContextHolder进行操作,该对象是个ThreadLocal级别数据。所以在处理完核心的doService逻辑后,最终会进行把RequestAttributes移除。RequestAttributes接口的实现类是ServletRequestAttributes,所以该类中对于属性的设置,其实底层都会设置到HttpServletRequest中。最后也会发现,每个请求执行完,都会发送ServletRequestHandledEvent事件。逻辑的核心在于doService,该方法是个抽象方法,由DispatcherServlet实现。
//FrameworkServlet#processRequest
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
initContextHolders(request, localeContext, requestAttributes);
try {
doService(request, response);
} finally {
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
logResult(request, response, failureCause, asyncManager);
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
在doService逻辑中,会先设置属性到HttpServletRequest中,然后调用doDispatch方法,进行分发逻辑,该方法中承载了整个MVC处理的逻辑。
//DispatcherServlet#doService
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
logRequest(request);
// Make framework objects available to handlers and view objects.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
try {
doDispatch(request, response);
}
}
doDispatch核心逻辑如下,每个逻辑需要单独解析
- 判断是否上传文件,如果有上传文件则需要进行解析出来。
- 获取HandlerMapping
- 获取HandlerAdapter
- 调用HandlerInterceptor接口的preHandle方法
- 调用Handler
- 调用HandlerInterceptor接口的postHandle方法
- 分发结果处理
//DispatcherServlet#doDispatch
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 {
//1. 是否上传文件
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
//2. 获取HandlerMapping
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
//3. 获取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;
}
}
//4. 调用HandlerInterceptor接口的preHandle方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
//5. 调用Handler
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
//6. 调用HandlerInterceptor接口的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);
}
//7. 分发结果处理
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
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);
}
}
}
}
整体处理逻辑如下图所示:和上述逻辑基本一样,现在基本上是前后端分离项目,所以关于视图解析的内容,不再展开。会补充文件解析以及HandlerInterceptor相关的内容。
2. MultipartResolver
首先会判断是否有MultipartResolver,如果有的话,会调用resolveMultipart方法。在SpringMVC中,默认是没有MultipartResolver的Bean的,如果需要则要自己注入一个名称为“multipartResolver”的MultipartResolver。MultipartResolver有两个实现基于Servlet3.0实现的StandardServletMultipartResolver,以及基于Apache Commons FileUpload 1.2实现的CommonsMultipartResolver
//DispatcherServlet#checkMultipart
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {
}else {
try {
return this.multipartResolver.resolveMultipart(request);
}
}
// If not returned before: return original request.
return request;
}
以下内容分析StandardServletMultipartResolver,在isMultipart方法中,判断Content-Type是否是“multipart/”开头,如果是则说明是文件上传。
//StandardServletMultipartResolver#isMultipart
public boolean isMultipart(HttpServletRequest request) {
return StringUtils.startsWithIgnoreCase(request.getContentType(), "multipart/");
}
在resolveMultipart方法中,会返回一个StandardMultipartHttpServletRequest
//StandardServletMultipartResolver#resolveMultipart
public MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException {
return new StandardMultipartHttpServletRequest(request, this.resolveLazily);
}
StandardMultipartHttpServletRequest也实现了HttpServletRequest接口,构造器会把Request传给父类存储,lazyParsing在默认情况下为false,也就是会做请求的解析。
//StandardMultipartHttpServletRequest
public StandardMultipartHttpServletRequest(HttpServletRequest request, boolean lazyParsing)
throws MultipartException {
super(request);
if (!lazyParsing) {
parseRequest(request);
}
}
parseRequest方法中,会先从Request中获取Part数据,该步骤会触发文件的读取。在Tomcat的实现中,实际会把读取的文件存储到临时文件,然后构造文件信息的Part的返回,业务从Part读取数据,实际是从文件中读取。
然后会遍历Part,获取名称和MultipartFile的映射关系,便于后续环节使用。
parseRequest方法中,实际上会触发网络数据读取,可能会出现时失败,当失败的时候,实际还没有执行到HandlerMapping。
//StandardMultipartHttpServletRequest#parseRequest
private void parseRequest(HttpServletRequest request) {
try {
Collection<Part> parts = request.getParts();
this.multipartParameterNames = new LinkedHashSet<>(parts.size());
MultiValueMap<String, MultipartFile> files = new LinkedMultiValueMap<>(parts.size());
for (Part part : parts) {
String headerValue = part.getHeader(HttpHeaders.CONTENT_DISPOSITION);
ContentDisposition disposition = ContentDisposition.parse(headerValue);
String filename = disposition.getFilename();
if (filename != null) {
if (filename.startsWith("=?") && filename.endsWith("?=")) {
filename = MimeDelegate.decode(filename);
}
files.add(part.getName(), new StandardMultipartFile(part, filename));
}
else {
this.multipartParameterNames.add(part.getName());
}
}
setMultipartFiles(files);
}
catch (Throwable ex) {
handleParseFailure(ex);
}
}
3. HanderMapping
默认的HanderMapping会有两个,在DispatcherServlet.properties文件中定义,分别是RequestMappingHandlerMapping、BeanNameUrlHandlerMapping
3.1 HandlerMapping定义
首先看一下HandlerMapping如何定义,在使用MVC的功能的时候,一般会启用注解@EnableWebMvc,在该注解中会Import一个DelegatingWebMvcConfiguration类,该类继承于WebMvcConfigurationSupport,会在WebMvcConfigurationSupport中定义一些Bean,其中就包括了HandlerMapping
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}
在创建RequestMappingHandlerMapping中会设置order,也会设置两个HandlerInterceptor,分别是ConversionServiceExposingInterceptor和ResourceUrlProviderExposingInterceptor
//WebMvcConfigurationSupport#requestMappingHandlerMapping
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
mapping.setOrder(0);
mapping.setInterceptors(getInterceptors());
return mapping;
}
//WebMvcConfigurationSupport#getInterceptors
protected final Object[] getInterceptors() {
if (this.interceptors == null) {
InterceptorRegistry registry = new InterceptorRegistry();
addInterceptors(registry);
registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService()));
registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider()));
this.interceptors = registry.getInterceptors();
}
return this.interceptors.toArray();
}
还会创建BeanNameUrlHandlerMapping,至于其他类型的HandlerMapping需要满足一定条件才会进行创建。
//WebMvcConfigurationSupport#beanNameHandlerMapping
@Bean
public BeanNameUrlHandlerMapping beanNameHandlerMapping() {
BeanNameUrlHandlerMapping mapping = new BeanNameUrlHandlerMapping();
mapping.setOrder(2);
mapping.setInterceptors(getInterceptors());
mapping.setCorsConfigurations(getCorsConfigurations());
return mapping;
}
此外WebMvcConfigurationSupport类还会创建其他组件,比如HandlerAdapter,分别是:RequestMappingHandlerAdapter、HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter,还会创建HandlerExceptionResolver。
我们已RequestMappingHandlerMapping为例,获取Handler的逻辑如下,RequestMappingHandlerMapping集成了AbstractHandlerMapping,主要流程有父类定义。首先会尝试获取Handler,如果找不到会找默认的Handler。如果Handler为字符串,则需要从IOC容器中获取实例,最后再封装成Chain,主要是方便执行拦截逻辑。
//AbstractHandlerMapping#getHandler
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
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 = obtainApplicationContext().getBean(handlerName);
}
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
return executionChain;
}
getHandlerInternal由子类实现,RequestMappingHandlerMapping也继承了AbstractHandlerMethodMapping,大致逻辑是会先逻辑要查找的路径,然后通过路径查找HandlerMethod然后返回。
//AbstractHandlerMethodMapping#getHandlerInternal
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
this.mappingRegistry.acquireReadLock();
try {
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
3.2 HandlerMethod维护
在查询HandlerMethod之前,我们看下HandlerMethod是怎么构建的。在AbstractHandlerMethodMapping的子类Bean初始化的时候(实现了InitializingBean接口,会在初始化的时候调用afterPropertiesSet方法)进行初始化。
//AbstractHandlerMethodMapping#afterPropertiesSet
public void afterPropertiesSet() {
initHandlerMethods();
}
会从ApplicationContext中获取所有bean,然后进行遍历处理。最后在执行HandlerMethod初始化,实际执行就是打印日志。
//AbstractHandlerMethodMapping#initHandlerMethods
protected void initHandlerMethods() {
for (String beanName : getCandidateBeanNames()) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
processCandidateBean(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
会先获取beanName对应的Class,然后判断是不是Handler,isHandler是个抽象方法,在RequestMappingHandlerMapping中的实现,就是判断是否有@Controller注解或者有@RequestMapping注解。
//AbstractHandlerMethodMapping#processCandidateBean
protected void processCandidateBean(String beanName) {
Class<?> beanType = null;
try {
beanType = obtainApplicationContext().getType(beanName);
}
catch (Throwable ex) {
}
if (beanType != null && isHandler(beanType)) {
detectHandlerMethods(beanName);
}
}
//RequestMappingHandlerMapping#isHandler
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
detectHandlerMethods内部会通过getMappingForMethod方法获取映射关系,然后在进行注册HandlerMethod。
//AbstractHandlerMethodMapping#detectHandlerMethods
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
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);
registerHandlerMethod(handler, invocableMethod, mapping);
});
getMappingForMethod是一个抽象方法,RequestMappingHandlerMapping中实现为先创建RequestMappingInfo,然后返回。创建RequestMappingInfo的逻辑就是从Method中获取RequestMapping注解,然后封装成RequestMappingInfo对象。
//RequestMappingHandlerMapping#getMappingForMethod
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMappingInfo info = createRequestMappingInfo(method);
if (info != null) {
RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
if (typeInfo != null) {
info = typeInfo.combine(info);
}
String prefix = getPathPrefix(handlerType);
if (prefix != null) {
info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
}
}
return info;
}
//RequestMappingHandlerMapping#createRequestMappingInfo
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
RequestCondition<?> condition = (element instanceof Class ?
getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}
在registerHandlerMethod方法中,实际会调用MappingRegistry的register方法。
//AbstractHandlerMethodMapping#registerHandlerMethod
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
this.mappingRegistry.register(mapping, handler, method);
}
在register方法中,首先会把Handler和Method封装成HandlerMethod,然后会加入到mappingLookup中,形成Mapping ->HandlerMethod的关系。然后会计算Mapping的url,加入到urlLookup中,形成url -> Mapping的映射关系。最后还会加到到registry中,形成Mapping -> MappingRegistration的关系。
在RequestMappingHandlerMapping方法中,Mapping就是RequestMappingInfo对象。
//AbstractHandlerMethodMapping$MappingRegistry#register
public void register(T mapping, Object handler, Method method) {
this.readWriteLock.writeLock().lock();
try {
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
assertUniqueMethodMapping(handlerMethod, mapping);
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);
}
this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
}
finally {
this.readWriteLock.writeLock().unlock();
}
}
3.3 查找HandlerMethod
回到lookupHandlerMethod方法中,看一下查找逻辑。首先会从MappingRegistry中获取Mapping,也就是会到urlLookup中,根据url获取Mapping。如果有获取到则进行判断是否匹配。没有匹配的话,会尝试便利所有的Mapping。
这里循环遍历感觉性能不高,也没做缓存,不知道是否出于大多数都能通过url匹配的的情况。
//AbstractHandlerMethodMapping#lookupHandlerMethod
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);
}
addMatchingMappings中会对获取的Mapping进行判断,是否匹配。getMatchingMapping是个抽象方法,RequestMappingInfoHandlerMapping实现会直接调用RequestMappingInfo的getMatchingCondition方法进行判断。
RequestMappingInfoHandlerMapping的Mapping就是RequestMappingInfo
//AbstractHandlerMethodMapping#addMatchingMappings
private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
for (T mapping : mappings) {
T match = getMatchingMapping(mapping, request);
if (match != null) {
matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
}
}
}
在getMatchingCondition,就是会判断请求的方法、参数、header、consume、等是否匹配,如果都匹配返回一个新的RequestMappingInfo
//RequestMappingInfo#getMatchingCondition
public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request);
if (methods == null) {
return null;
}
ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request);
if (params == null) {
return null;
}
HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request);
if (headers == null) {
return null;
}
ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request);
if (consumes == null) {
return null;
}
ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request);
if (produces == null) {
return null;
}
PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(request);
if (patterns == null) {
return null;
}
RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request);
if (custom == null) {
return null;
}
return new RequestMappingInfo(this.name, patterns,
methods, params, headers, consumes, produces, custom.getCondition());
}
如果找到了多个Mapping,则会进行排序,获取最符合的Mapping然后进行返回。
//AbstractHandlerMethodMapping#lookupHandlerMethod
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
matches.sort(comparator);
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
if (logger.isTraceEnabled()) {
logger.trace(matches.size() + " matching mappings: " + matches);
}
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();
String uri = request.getRequestURI();
throw new IllegalStateException(
"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
}
}
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
如果没有找到Mapping,则会进行处理,子类的实现(如RequestMappingInfoHandlerMapping,它是RequestMappingHandlerMapping的父类)会根据一些条件抛出异常。
//RequestMappingInfoHandlerMapping#handleNoMatch
protected HandlerMethod handleNoMatch(
Set<RequestMappingInfo> infos, String lookupPath, HttpServletRequest request) throws ServletException {
if (helper.hasProducesMismatch()) {
Set<MediaType> mediaTypes = helper.getProducibleMediaTypes();
throw new HttpMediaTypeNotAcceptableException(new ArrayList<>(mediaTypes));
}
}
回到getHandler逻辑中,因为获取到了Handler(实际就是HandlerMethod),会封装成HandlerExecutionChain。其实就是把adaptedInterceptors加入到HandlerExecutionChain中,便于做Handler的拦截处理。那么adaptedInterceptors是怎么维护的呢?
//AbstractHandlerMapping#getHandlerExecutionChain
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;
}
4. 获取HandlerAdapter
就是遍历handlerAdapters,看那个支持就返回那个HandlerAdapter。以RequestMappingHandlerAdapter为例
//DispatcherServlet#getHandlerAdapter
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
}
AbstractHandlerMethodAdapter是RequestMappingHandlerAdapter的父类,判断handler是HandlerMethod,且supportsInternal方法为true。我们在前面得得handler就HandlerMethod,而RequestMappingHandlerAdapter实现的supportsInternal方法默认就返回true。所以一般来说HandlerMapping和HandlerAdapter实际上就是成对出现的。RequestMappingHandlerMapping对应RequestMappingHandlerAdapter。
//AbstractHandlerMethodAdapter#supports
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
//RequestMappingHandlerAdapter#supportsInternal
protected boolean supportsInternal(HandlerMethod handlerMethod) {
return true;
}
5. 执行HandlerInterceptor
5.1 HandlerInterceptor维护
仅在SpringMVC的项目中,如果需要添加HandlerInterceptor,那么大致逻辑如下,会实现WebMvcConfigurer然后做@Configuration注解,该注解是实际上是@Component的派生注解,对应的类也会转换成Spring的Bean。
一般会这样定义,首先需要的HandlerInterceptor添加到Spring IOC容器中,然后通过addInterceptors逻辑注册到InterceptorRegistry里。
@Configuration
@EnableWebMvc
public class WebApplicationConfig implements WebMvcConfigurer {
@Autowired(required = false)
private List<HandlerInterceptor> handlerInterceptors;
@Override
public void addInterceptors(InterceptorRegistry registry) {
if (handlerInterceptors != null) {
for (HandlerInterceptor interceptor : handlerInterceptors) {
registry.addInterceptor(interceptor);
}
}
}
}
我们知道@EnableWebMvc实际上会Import DelegatingWebMvcConfiguration,该类内部会注入WebMvcConfigurer,这个正是前面我们设置的。然后添加到WebMvcConfigurerComposite,在Spring中,xxxComposite代表了组合,就是有多个实现,然后内部实现基本上是通过遍历调用,这里的逻辑是可能有多个WebMvcConfigurer,然后进行遍历调用。DelegatingWebMvcConfiguration同时也继承了WebMvcConfigurationSupport 。
@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.configurers.addWebMvcConfigurers(configurers);
}
}
@Override
protected void addInterceptors(InterceptorRegistry registry) {
this.configurers.addInterceptors(registry);
}
}
在前面我们知道RequestMappingHandlerMapping实际上就是WebMvcConfigurationSupport内创建的,所以HandlerMapping设置Interceptor也在RequestMappingHandlerMapping中。首先会尝试添加,添加实际就是会调用WebMvcConfigurer的addInterceptors逻辑,也就是会把自定义的HandlerInterceptor添加到InterceptorRegistry ,最后再获取所有的Interceptor。
//WebMvcConfigurationSupport#getInterceptors
protected final Object[] getInterceptors() {
if (this.interceptors == null) {
InterceptorRegistry registry = new InterceptorRegistry();
addInterceptors(registry);
registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService()));
registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider()));
this.interceptors = registry.getInterceptors();
}
return this.interceptors.toArray();
}
这其实也是Spring和SpringBoot的差别,Spring提供了各种API支持不同的能力,但是需要开发者自己去组装起来。而SpringBoot默认就进行了组装,达到开箱即用的效果
5.2 执行拦截器
以前置拦截器为例,其他拦截器的方法类似,只是时机和拦截器顺序不一定(比如postHandle是从后往前执行)
实际上就是根据之前设置的HandlerInterceptor然后遍历执行preHandle方法。记录interceptorIndex的值是为了只执行了preHandle的HandlerInterceptor,去执行它们的afterCompletion方法。整体逻辑还是比较简单,但也是个很好的扩展机制,值得学习。
//HandlerExecutionChain#applyPreHandle
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;
}
6. 执行HandlerAdapter
6.1 执行Handler
会用HandlerAdapter去执行Handler,返回ModelAndView。
以RequestMappingHandlerAdapter为例,实际调用handle是在父类AbstractHandlerMethodAdapter,内部会调用handleInternal方法,该方法是个抽象类。
内部主要两个逻辑,会做request的检查,如http 的method是否支持之类。然后执行invokeHandlerMethod方法,该方法是个核心的方法,逻辑比较多。
//RequestMappingHandlerAdapter#handleInternal
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
checkRequest(request);
mav = invokeHandlerMethod(request, response, handlerMethod);
return mav;
}
其中主要有4个逻辑,构建执行Handler的ServletInvocableHandlerMethod,会设置参数解析器,返回值处理器等;构建ModelAndViewContainer用于处理ModelAndView;调用Handler;返回ModelAndView。其中主要关心调用Handler
//RequestMappingHandlerAdapter#invokeHandlerMethod
protected ModelAndView invokeHandlerMethod() {
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
//构建ServletInvocableHandlerMethod
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if(this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if(this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
//构建ModelAndViewContainer
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
//调用Handler
invocableMethod.invokeAndHandle(webRequest, mavContainer);
//获取ModelAndView
return getModelAndView(mavContainer, modelFactory, webRequest);
}
invokeAndHandle内部,首先会调用Handler,然后对于返回的结果进行处理。如果结果为空,或者设置了@ResponseStatus注解的reason,则会直接返回,并且标记requestHandled=true,有该标记,后面流程中获取ModelAndView会直接返回,不去获取。最后会通过HandlerMethodReturnValueHandlerComposite对返回值进行解析。
//ServletInvocableHandlerMethod#invokeAndHandle
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
disableContentCachingIfNecessary(webRequest);
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
try {
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
}
HandlerMethod实际就是作用在方法上,所以invokeForRequest会先获取Method所需的参数,最后会通过反射进行调用。
//InvocableHandlerMethod#invokeForRequest
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);
}
获取参数内部的逻辑,实际会遍历参数,然后判断参数符合哪个HandlerMethodArgumentResolver,然后进行解析。以参数中带有HttpServletRequest为例,对应的HandlerMethodArgumentResolver是ServletRequestMethodArgumentResolver,该类内部支持解析多种接口。
//ServletRequestMethodArgumentResolver#supportsParameter
public boolean supportsParameter(MethodParameter parameter) {
Class<?> paramType = parameter.getParameterType();
return (WebRequest.class.isAssignableFrom(paramType) ||
ServletRequest.class.isAssignableFrom(paramType) ||
MultipartRequest.class.isAssignableFrom(paramType) ||
HttpSession.class.isAssignableFrom(paramType) ||
(pushBuilder != null && pushBuilder.isAssignableFrom(paramType)) ||
Principal.class.isAssignableFrom(paramType) ||
InputStream.class.isAssignableFrom(paramType) ||
Reader.class.isAssignableFrom(paramType) ||
HttpMethod.class == paramType ||
Locale.class == paramType ||
TimeZone.class == paramType ||
ZoneId.class == paramType);
}
HttpServletRequest的参数,内部就会进行参数的解析,然后进行返回。
//ServletRequestMethodArgumentResolver#resolveArgument
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
Class<?> paramType = parameter.getParameterType();
//...
// ServletRequest / HttpServletRequest / MultipartRequest / MultipartHttpServletRequest
if (ServletRequest.class.isAssignableFrom(paramType) || MultipartRequest.class.isAssignableFrom(paramType)) {
return resolveNativeRequest(webRequest, paramType);
}
}
//ServletRequestMethodArgumentResolver#resolveNativeRequest
private <T> T resolveNativeRequest(NativeWebRequest webRequest, Class<T> requiredType) {
T nativeRequest = webRequest.getNativeRequest(requiredType);
return nativeRequest;
}
handleReturnValue内部逻辑会先获取一个HandlerMethodReturnValueHandler,然后执行handleReturnValue方法
//HandlerMethodReturnValueHandlerComposite#handleReturnValue
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
}
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
selectHandler的逻辑是,通过supportsReturnType逻辑判断是否支持该返回值,支持旧选择该HandlerMethodReturnValueHandler。
//HandlerMethodReturnValueHandlerComposite#selectHandler
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
boolean isAsyncValue = isAsyncReturnValue(value, returnType);
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
continue;
}
if (handler.supportsReturnType(returnType)) {
return handler;
}
}
return null;
}
当前项目一般都是前段端分离项目,所以我们就以RequestResponseBodyMethodProcessor(实现了HandlerMethodReturnValueHandler接口)为例,看下supportsReturnType和handleReturnValue方法如何处理
supportsReturnType的判断逻辑就是,查看方法或者类中是否有ResponseBody注解。
//RequestResponseBodyMethodProcessor#supportsReturnType
public boolean supportsReturnType(MethodParameter returnType) {
return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
returnType.hasMethodAnnotation(ResponseBody.class));
}
handleReturnValue的逻辑,首先会把requestHandled设置为true,该逻辑前面有说过,设置为true后续将不会执行获取ModelAndView的逻辑,核心的逻辑在writeWithMessageConverters中。
//RequestResponseBodyMethodProcessor#handleReturnValue
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
mavContainer.setRequestHandled(true);
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
// Try even with null return value. ResponseBodyAdvice could get involved.
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
writeWithMessageConverters内主要有两大逻辑,第一:判断合适的MediaType。第二根据选择的MediaType查找合适的HttpMessageConverter,然后执行write方法。判断MediaType不展开分析,主要是根据返回值、返回类型、请求头、相应的Content-Type进行判断。HttpMessageConverter的处理主要是两个接口,符合canWrite之后会调用write。
//AbstractMessageConverterMethodProcessor#writeWithMessageConverters
converter.canWrite(valueType, selectedMediaType)
((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
下面我们就以GsonHttpMessageConverter为例,看一下这两个接口如何支持。
canWrite接口主要判断supports,该接口在子类中默认为true;canWrite主要判断MediaType是否符合,返回类型为application/json的能处理
//AbstractHttpMessageConverter#canWrite
public boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) {
return supports(clazz) && canWrite(mediaType);
}
//AbstractGenericHttpMessageConverter#supports
protected boolean supports(Class<?> clazz) {
return true;
}
GsonHttpMessageConverter的父类AbstractJsonHttpMessageConverter会设置supportedMediaTypes为new MediaType("application", "*+json")
//AbstractHttpMessageConverter#canWrite
boolean canWrite(@Nullable MediaType mediaType) {
if (mediaType == null || MediaType.ALL.equalsTypeAndSubtype(mediaType)) {
return true;
}
for (MediaType supportedMediaType : getSupportedMediaTypes()) {
if (supportedMediaType.isCompatibleWith(mediaType)) {
return true;
}
}
return false;
}
write接口会不断的调用writeInternal,让子类进行处理,最后到GsonHttpMessageConverter时,就会用Gson.toJson把对象转成json,并且写入到Response中,这样就完成了body的返回,写完后还会调用flush。
//AbstractHttpMessageConverter#write
public final void write(final T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
final HttpHeaders headers = outputMessage.getHeaders();
addDefaultHeaders(headers, t, contentType);
writeInternal(t, outputMessage);
outputMessage.getBody().flush();
}
//AbstractJsonHttpMessageConverter#writeInternal
protected final void writeInternal(Object object, @Nullable Type type, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
Writer writer = getWriter(outputMessage);
if (this.jsonPrefix != null) {
writer.append(this.jsonPrefix);
}
try {
writeInternal(object, type, writer);
}
catch (Exception ex) {
throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
}
writer.flush();
}
//GsonHttpMessageConverter#writeInternal
protected void writeInternal(Object object, @Nullable Type type, Writer writer) throws Exception {
if (type instanceof ParameterizedType) {
getGson().toJson(object, type, writer);
}
else {
getGson().toJson(object, writer);
}
}
6.2 获取ModelAndView
前面有说过,会设置requestHandled为true,所以这里会返回null。
//RequestMappingHandlerAdapter#getModelAndView
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
modelFactory.updateModel(webRequest, mavContainer);
if (mavContainer.isRequestHandled()) {
return null;
}
}
回到DispatcherServlet里,会对ModelAndView进行处理,但是前面已经返回了null,所以这里也就不处理了。
//DispatcherServlet#processDispatchResult
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
7. 异常解析
handler执行完后,会处理结果,其中就有Exception的判断,内部会通过processHandlerException方法处理
//DispatcherServlet#processDispatchResult
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);
}
}
异常解析遍历handlerExceptionResolvers,而默认内置的异常解析器ExceptionHandlerExceptionResolver、ResponseStatusExceptionResolver、DefaultHandlerExceptionResolver
//DispatcherServlet#processHandlerException
if (this.handlerExceptionResolvers != null) {
for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
exMv = resolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
}
以DefaultHandlerExceptionResolver为例,内部会判断各种异常,然后返回对应的响应值。
//DefaultHandlerExceptionResolver#doResolveException
if (ex instanceof HttpRequestMethodNotSupportedException) {
return handleHttpRequestMethodNotSupported(
(HttpRequestMethodNotSupportedException) ex, request, response, handler);
}
//DefaultHandlerExceptionResolver#handleHttpRequestMethodNotSupported
protected ModelAndView handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex,
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {
String[] supportedMethods = ex.getSupportedMethods();
if (supportedMethods != null) {
response.setHeader("Allow", StringUtils.arrayToDelimitedString(supportedMethods, ", "));
}
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, ex.getMessage());
return new ModelAndView();
}
8. 附录
8.1 Spring整合内嵌Tomcat
Tomcat的gradle依赖:org.apache.tomcat.embed:tomcat-embed-core:9.0.36
、以及Spring MVC版本
启动类
public class WebApplicationStarter {
private Tomcat tomcat;
public static void main(String[] args) throws LifecycleException {
WebApplicationStarter starter = new WebApplicationStarter();
starter.start();
}
public WebApplicationStarter(){
tomcat = new Tomcat();
tomcat.setPort(8080);
tomcat.getConnector();
Context context = tomcat.addContext("", null);
try {
DispatcherServlet dispatcherServlet = new DispatcherServlet(createContext(context.getServletContext()));
Wrapper wrapper = Tomcat.addServlet(context, "dispatcherServlet", dispatcherServlet);
wrapper.setLoadOnStartup(1);
wrapper.addMapping("/*");
}catch (Exception e) {
e.printStackTrace();
}
}
public void start() throws LifecycleException {
if (this.tomcat != null) {
this.tomcat.start();
}
}
public WebApplicationContext createContext(ServletContext context){
AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
applicationContext.register(WebApplicationConfig.class);
applicationContext.setServletContext(context);
applicationContext.refresh();
applicationContext.registerShutdownHook();
return applicationContext;
}
}
配置类
@Configuration
@ComponentScan(basePackageClasses = WebApplicationConfig.class)
@EnableWebMvc
public class WebApplicationConfig implements WebMvcConfigurer {
@Autowired(required = false)
private List<HandlerInterceptor> handlerInterceptors;
@Override
public void addInterceptors(InterceptorRegistry registry) {
if (handlerInterceptors != null) {
for (HandlerInterceptor interceptor : handlerInterceptors) {
registry.addInterceptor(interceptor);
}
}
}
}
控制器
@RestController
public class TestController {
@GetMapping("/test")
public String test(HttpServletRequest request, @RequestParam("size") Integer size) {
return "test";
}
}
9. 参考资料
- Spring源码分支5.1.x