目录
一、HandlerMapping
- 继承体系
- AbstractHandlerMapping
- AbstractUrlHandlerMapping系列
- AbstractHandlerMethodMapping系列
二、HandlerAdapter
- RequestMappingHandlerAdapter概述
- RequestMappingHandlerAdapter自身结构
- 总结
三、ViewResolver
四、RequestToViewTranslator
五、HandlerExceptionResolver
六、MultipartResolver
- StandardServletMultipartResolver
七、SpringMVC原理总结
一、HandlerMapping
1、继承体系
可以看到HandlerMapping家族的成员可以分为两支,其下第一个抽象类为AbstractHandlerMapping
- 一支继承AbstractUrlHandlerMapping
- 一支继承AbstractHandlerMethodMapping
2、AbstractHandlerMapping
概述
- 是HandlerMapping的抽象实现,下面所有的XxxHandlerMapping都继承他
- 采用模板模式设计了HandlerMapiing实现的整体结构,子类只需要通过模板方法提供一些初始值或者具体的算法即可,首先使用一个抽象类实现采用模板模式进行整体设计,然后子类通过实现模板方法具体完成业务。
- HandlerMapping的作用是根据request查找Handler和Interceptors,获取Handler的过程通过模板方法getHandlerInternal交给了子类。
- AbstractHandlerMapping保存了所有配置的Interceptor,在获取到Handler之后会自己根据从request提取的lookupPath将相应的Interceptors装配上去
- 子类可以通过getHandlerinternal方法设置自己的Interceptor,返回值类型为Object
2.1创建AbstractHandlerMapping之器
概述
- 先看类继承实现
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered, BeanNameAware {} - 父类WebApplicationObjectSupport ,初始化会自动调用模板方法initApplicationContext,,也就是子类AbstractHandlerMapping的入口方法
- AbstractHandlerMapping的创建就是在initApplicationContext方法里面实现的
protected void initApplicationContext(ApplicationContext context) {
super.initApplicationContext(context);
if (this.servletContext == null && context instanceof WebApplicationContext) {
this.servletContext = ((WebApplicationContext)context).getServletContext();
if (this.servletContext != null) {
this.initServletContext(this.servletContext);
}
}
}
protected void initApplicationContext() throws BeansException {
this.extendInterceptors(this.interceptors);
this.detectMappedInterceptors(this.adaptedInterceptors);
this.initInterceptors();
}
private final List<Object> interceptors = new ArrayList();
private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList();
protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
mappedInterceptors.addAll(BeanFactoryUtils.beansOfTypeIncludingAncestors(this.obtainApplicationContext(), MappedInterceptor.class, true, false).values());
}
protected void initInterceptors() {
if (!this.interceptors.isEmpty()) {
for(int i = 0; i < this.interceptors.size(); ++i) {
Object interceptor = this.interceptors.get(i);
if (interceptor == null) {
throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
}
this.adaptedInterceptors.add(this.adaptInterceptor(interceptor));
}
}
}
成员属性概述
-
List<Object> interceptors :用于配置SpringMVC的拦截器,有两种设置方式①注册HandlerMapping时通过属性设置②通过子类的extendInterceptors钩子方法进行设置。 并不会直接使用,而是通过initInterceptors()方法装配到adaptedInterceptors()才能使用。 -
List<HandlerInterceptor> adaptedInterceptors :这种类型的Interceptor不需要在进行匹配,在getHandler中会全部添加到返回值HnadlerExecutionChain里面,或者是使用时与请求的url进行匹配,从detectMappedInterceptors()方法获取添加到HnadlerExecutionChain里面
2.2AbstractHandlerMapping之用
HandlerMapping是通过getHandler方法处理处理器Handler和Interceptor的,实现分为两部分
- 第一部分:找Handler
- 第二部分:添加Interceptors
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = this.getHandlerInternal(request);
if (handler == null) {
handler = this.getDefaultHandler();
}
if (handler == null) {
return null;
} else {
if (handler instanceof String) {
String handlerName = (String)handler;
handler = this.obtainApplicationContext().getBean(handlerName);
}
HandlerExecutionChain executionChain = this.getHandlerExecutionChain(handler, request);
if (this.logger.isTraceEnabled()) {
this.logger.trace("Mapped to " + handler);
} else if (this.logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
this.logger.debug("Mapped to " + executionChain.getHandler());
}
if (this.hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
CorsConfiguration config = this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null;
CorsConfiguration handlerConfig = this.getCorsConfiguration(handler, request);
config = config != null ? config.combine(handlerConfig) : handlerConfig;
executionChain = this.getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
}
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = handler instanceof HandlerExecutionChain ? (HandlerExecutionChain)handler : new HandlerExecutionChain(handler);
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, LOOKUP_PATH);
Iterator var5 = this.adaptedInterceptors.iterator();
while(var5.hasNext()) {
HandlerInterceptor interceptor = (HandlerInterceptor)var5.next();
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor)interceptor;
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
} else {
chain.addInterceptor(interceptor);
}
}
return chain;
}
3、AbstractUrlHandlerMapping系列
概述
- 该系列的类都继承AbstractUrlHandlerMapping,它是根据url匹配的。
- 此系列的大致原理是将url和对应的handler保存在一个Map中,在getHandlerInternal方法使用url从Map中获Handler
- AbstractUrlHandlerMapping完成了url查找handler的过程,对于map的初始化工作交给子类完成,这里的map就是定义在AbstractUrlHandlerMapping的成员变量
handlerMap - 另外还定义了处理
/ 请求的rootHandler,定义在成员变量private Object rootHandler
主要问题就是
- 怎么获取handler的?
- 这个Map是由子类怎么创建的?
3.1根据URL获取Handler
获取handler的入口是在子类 实现AbstractHandlerMapping模板方法getHandlerInternal()里面,也就是AbstractUrlHandlerMapping的getHandlerInternal()
@Nullable
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = this.getUrlPathHelper().getLookupPathForRequest(request);
request.setAttribute(LOOKUP_PATH, lookupPath);
Object handler = this.lookupHandler(lookupPath, request);
if (handler == null) {
Object rawHandler = null;
if (StringUtils.matchesCharacter(lookupPath, '/')) {
rawHandler = this.getRootHandler();
}
if (rawHandler == null) {
rawHandler = this.getDefaultHandler();
}
if (rawHandler != null) {
if (rawHandler instanceof String) {
String handlerName = (String)rawHandler;
rawHandler = this.obtainApplicationContext().getBean(handlerName);
}
this.validateHandler(rawHandler, request);
handler = this.buildPathExposingHandler(rawHandler, lookupPath, lookupPath, (Map)null);
}
}
return handler;
}
@Nullable
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
Object handler = this.handlerMap.get(urlPath);
if (handler != null) {
if (handler instanceof String) {
String handlerName = (String)handler;
handler = this.obtainApplicationContext().getBean(handlerName);
}
this.validateHandler(handler, request);
return this.buildPathExposingHandler(handler, urlPath, urlPath, (Map)null);
} else {
List<String> matchingPatterns = new ArrayList();
Iterator var5 = this.handlerMap.keySet().iterator();
while(var5.hasNext()) {
String registeredPattern = (String)var5.next();
if (this.getPathMatcher().match(registeredPattern, urlPath)) {
matchingPatterns.add(registeredPattern);
} else if (this.useTrailingSlashMatch() && !registeredPattern.endsWith("/") && this.getPathMatcher().match(registeredPattern + "/", urlPath)) {
matchingPatterns.add(registeredPattern + "/");
}
}
String bestMatch = null;
Comparator<String> patternComparator = this.getPathMatcher().getPatternComparator(urlPath);
if (!matchingPatterns.isEmpty()) {
matchingPatterns.sort(patternComparator);
if (this.logger.isTraceEnabled() && matchingPatterns.size() > 1) {
this.logger.trace("Matching patterns " + matchingPatterns);
}
bestMatch = (String)matchingPatterns.get(0);
}
if (bestMatch != null) {
handler = this.handlerMap.get(bestMatch);
if (handler == null) {
if (bestMatch.endsWith("/")) {
handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));
}
if (handler == null) {
throw new IllegalStateException("Could not find handler for best pattern match [" + bestMatch + "]");
}
}
String pathWithinMapping;
if (handler instanceof String) {
pathWithinMapping = (String)handler;
handler = this.obtainApplicationContext().getBean(pathWithinMapping);
}
this.validateHandler(handler, request);
pathWithinMapping = this.getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);
Map<String, String> uriTemplateVariables = new LinkedHashMap();
Iterator var9 = matchingPatterns.iterator();
while(var9.hasNext()) {
String matchingPattern = (String)var9.next();
if (patternComparator.compare(bestMatch, matchingPattern) == 0) {
Map<String, String> vars = this.getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
Map<String, String> decodedVars = this.getUrlPathHelper().decodePathVariables(request, vars);
uriTemplateVariables.putAll(decodedVars);
}
}
if (this.logger.isTraceEnabled() && uriTemplateVariables.size() > 0) {
this.logger.trace("URI variables " + uriTemplateVariables);
}
return this.buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
} else {
return null;
}
}
}
protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern, String pathWithinMapping, @Nullable Map<String, String> uriTemplateVariables) {
HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);
chain.addInterceptor(new AbstractUrlHandlerMapping.PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));
if (!CollectionUtils.isEmpty(uriTemplateVariables)) {
chain.addInterceptor(new AbstractUrlHandlerMapping.UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));
}
return chain;
}
private class PathExposingHandlerInterceptor extends HandlerInterceptorAdapter {
private final String bestMatchingPattern;
private final String pathWithinMapping;
public PathExposingHandlerInterceptor(String bestMatchingPattern, String pathWithinMapping) {
this.bestMatchingPattern = bestMatchingPattern;
this.pathWithinMapping = pathWithinMapping;
}
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
AbstractUrlHandlerMapping.this.exposePathWithinMapping(this.bestMatchingPattern, this.pathWithinMapping, request);
request.setAttribute(HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE, handler);
request.setAttribute(HandlerMapping.INTROSPECT_TYPE_LEVEL_MAPPING, AbstractUrlHandlerMapping.this.supportsTypeLevelMappings());
return true;
}
}
private class UriTemplateVariablesHandlerInterceptor extends HandlerInterceptorAdapter {
private final Map<String, String> uriTemplateVariables;
public UriTemplateVariablesHandlerInterceptor(Map<String, String> uriTemplateVariables) {
this.uriTemplateVariables = uriTemplateVariables;
}
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
AbstractUrlHandlerMapping.this.exposeUriTemplateVariables(this.uriTemplateVariables, request);
return true;
}
}
protected void exposePathWithinMapping(String bestMatchingPattern, String pathWithinMapping, HttpServletRequest request) {
request.setAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE, bestMatchingPattern);
request.setAttribute(PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, pathWithinMapping);
}
protected void exposeUriTemplateVariables(Map<String, String> uriTemplateVariables, HttpServletRequest request) {
request.setAttribute(URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVariables);
}
3.2映射放入Map的逻辑
概述
- 通过registerHandler()方法进行Map初始化,这个方法承担AbstractUrlHandlerMapping的创建工作,不过和之前不同的是,这里的registerHandler()并不是自己调用,也不是父类AbstractHandlerMapping调用,而是子类调用,这样不同的子类就可以通过注册不同的Hnadler将组件创建出来。这是新的思路。
- AbstractUrlHandlerMapping中有两个registerHandler方法,也就是保存映射关系到Map
- 第一个:注册多个url到同一个handler,处理器用的就是String类型的beanName,它是实用beanName到spring容器寻找真实的bean做处理器。这个方法其实就是遍历全部Url,调用第二个方法,将handler注册到Map
- 第二个:先看Map里原来有无url,没有就put进去,有的话就对比旧值和现在的值,相等的话就证明url对应两个handler,直接抛出异常。另外放的时候需要判断url是不是
/ 和/* 如果是就不往Map里放了,而是分别设置到rootHandler和defaultHandler
protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException {
Assert.notNull(urlPaths, "URL path array must not be null");
String[] var3 = urlPaths;
int var4 = urlPaths.length;
for(int var5 = 0; var5 < var4; ++var5) {
String urlPath = var3[var5];
this.registerHandler((String)urlPath, (Object)beanName);
}
}
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
Assert.notNull(urlPath, "URL path must not be null");
Assert.notNull(handler, "Handler object must not be null");
Object resolvedHandler = handler;
if (!this.lazyInitHandlers && handler instanceof String) {
String handlerName = (String)handler;
ApplicationContext applicationContext = this.obtainApplicationContext();
if (applicationContext.isSingleton(handlerName)) {
resolvedHandler = applicationContext.getBean(handlerName);
}
}
Object mappedHandler = this.handlerMap.get(urlPath);
if (mappedHandler != null) {
if (mappedHandler != resolvedHandler) {
throw new IllegalStateException("Cannot map " + this.getHandlerDescription(handler) + " to URL path [" + urlPath + "]: There is already " + this.getHandlerDescription(mappedHandler) + " mapped.");
}
} else if (urlPath.equals("/")) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Root mapping to " + this.getHandlerDescription(handler));
}
this.setRootHandler(resolvedHandler);
} else if (urlPath.equals("/*")) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Default mapping to " + this.getHandlerDescription(handler));
}
this.setDefaultHandler(resolvedHandler);
} else {
this.handlerMap.put(urlPath, resolvedHandler);
if (this.logger.isTraceEnabled()) {
this.logger.trace("Mapped [" + urlPath + "] onto " + this.getHandlerDescription(handler));
}
}
}
3.3子类SimpleUrlHandlerMapping
概述
- 内部定义了Map变量,自己定义一个Map的好处是方便配置,而且可以在注册前做一些已处理,如确保所有的url都以
/ 开头,将所有的url 和 handler 的映射关系放入Map,之后注册到父类的Map中 - 该类创建时通过重写父类的initApplicationContext()方法,内部调用了 自己的registerHandlers()
- 自己的registerHandlers()又调用了父类的AbstractUrlHandlerMapping的registerHandler()将这里的到的urlMap注册到AbstractUrlHandlerMapping的Map中(也就是子类map先初始化,最后合并到父类map)
- 另外使用SimpleUrlHandlerMapping就需要在注册时给他设置urlMap
private final Map<String, Object> urlMap = new LinkedHashMap();
public SimpleUrlHandlerMapping(Map<String, ?> urlMap) {
this.setUrlMap(urlMap);
}
public SimpleUrlHandlerMapping(Map<String, ?> urlMap, int order) {
this.setUrlMap(urlMap);
this.setOrder(order);
}
public void setMappings(Properties mappings) {
CollectionUtils.mergePropertiesIntoMap(mappings, this.urlMap);
}
public void setUrlMap(Map<String, ?> urlMap) {
this.urlMap.putAll(urlMap);
}
public void initApplicationContext() throws BeansException {
super.initApplicationContext();
this.registerHandlers(this.urlMap);
}
protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
if (urlMap.isEmpty()) {
this.logger.trace("No patterns in " + this.formatMappingName());
} else {
urlMap.forEach((url, handler) -> {
if (!url.startsWith("/")) {
url = "/" + url;
}
if (handler instanceof String) {
handler = ((String)handler).trim();
}
this.registerHandler(url, handler);
});
if (this.logger.isDebugEnabled()) {
List<String> patterns = new ArrayList();
if (this.getRootHandler() != null) {
patterns.add("/");
}
if (this.getDefaultHandler() != null) {
patterns.add("/**");
}
patterns.addAll(this.getHandlerMap().keySet());
this.logger.debug("Patterns " + patterns + " in " + this.formatMappingName());
}
}
}
3.4子类AbstractDetecingUrlHandlerMapping
概述
- 也是通过重写initApplicationContext()来注册Handler的,里面调用了detectHandlers()
- 在detectHandlers()内部根据配置的detectHandlersInAncestorContexts参数从SpringMVC 或者 父容器找出所有的beanName
- 根据beanName调用找到对应的urls,解析结果不为空就解析出urls和beanName(作为handler)注册到父类的Map
- 也有是3个子类BeanNameUrlHandlerMapping、DefaultAnnontationHandlerMapping(弃用)和AbstractControllerUrlHandlerMapping
- BeanNameUrlHandlerMapping:检查beanName和alias是不是以
/ 开头,如果是则将其作为url,里面只有一个determineUrlsForHandler()方法 - AbstractControllerUrlHandlerMapping:将实现了Controllet接口或者是标注@Controller的bean作为Handler,并且可以设置excludedClasses和excludedPackages将不包含的bean或者不包含的包下的bean排除在外
public void initApplicationContext() throws ApplicationContextException {
super.initApplicationContext();
this.detectHandlers();
}
protected void detectHandlers() throws BeansException {
ApplicationContext applicationContext = this.obtainApplicationContext();
String[] beanNames = this.detectHandlersInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) : applicationContext.getBeanNamesForType(Object.class);
String[] var3 = beanNames;
int var4 = beanNames.length;
for(int var5 = 0; var5 < var4; ++var5) {
String beanName = var3[var5];
String[] urls = this.determineUrlsForHandler(beanName);
if (!ObjectUtils.isEmpty(urls)) {
this.registerHandler(urls, beanName);
}
}
if (this.logger.isDebugEnabled() && !this.getHandlerMap().isEmpty() || this.logger.isTraceEnabled()) {
this.logger.debug("Detected " + this.getHandlerMap().size() + " mappings in " + this.formatMappingName());
}
}
4、AbstractHandlerMethodMapping系列
概述
- 从上面的HandlerMapping的结构图可以看出,AbstractHandlerMethodMapping系列的结构很简单,只有三个类
- AbstractHandlerMethodMapping
- RequestMappingInfoHandlerMapping
- RequestMappingHandlerMapping
- 该系列是将method作为Handler使用,这也是目前用的最多的一种Handler,如经常使用的@RequestMapping注解方法就是这种Handler,他有一个专门的类型 HandlerMethod 也就是Method类型的Handler
4.1 创建AbstractHandlerMethodMapping之器
AbstractHandlerMethodMapping<T> 对于泛型T,也就是RequestCondition 接口的实现类
- 表示用来匹配Handler的条件专门使用的一种类,这里的条件不只是url了,还可以有很多其他条件,如request的类型、请求的参数、Header都可用作为匹配HandlerMethod的条件
- 默认使用的是RequestMappingInfo,从RequestMappingInfoHandlerMapping就可以看出
public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMethodMapping<RequestMappingInfo>
RequestCondition的实现类
-
RequestCondition接口的实现类1—》抽象类AbstractRequestCondition中重写了equals、hashCode、toString三个方法,有8个子类,对应8中匹配条件
- PatternsRequestCondition使用url做匹配
- RequestMethodsRequestCondition使用RequestMethod做匹配
- CompositeRequestCondition本身并不实际做匹配,而是可以将多个别的RquestCondition封装到自己的成员变量,在用的时候遍历进行匹配,这也是大家所熟悉的责任链模式,在SpringMVC中XxxComposite形式,他们的主要目的就是为了方便调用
-
RequestCondition接口的实现类2—》RequestMappingInfo,它里面其实使用7个变量保存了7个RequestCondition,在匹配时使用那7个变量进行匹配,这也就是可以在@RequestMapping中给处理器指定多种匹配方式的原因
AbstractHandlerMethodMapping系列创建的步骤
- AbstractHandlerMethodMapping实现了InitializingBean接口,所以spring容器会自动调用afterProperties()方法
- afterProperties()方法又交给了initHandlerMethods方法完成具体的初始化
- initHandlerMethods方法选出容器的所有Bean,然后模板方法isHandler()判断选出标注@Controller的Handler(子类RequestMappingHandlerMapping实现isHandler模板方法)
- 之后this.detectHandlerMethods(beanName)保存Handler到Map,分两步走,首先根据传入的handler找到符合要求的方法,然后使用registerHandlerMethod保存到Map.
public void afterPropertiesSet() {
this.initHandlerMethods();
int total = this.getHandlerMethods().size();
if (this.logger.isTraceEnabled() && total == 0 || this.logger.isDebugEnabled() && total > 0) {
this.logger.debug(total + " mappings in " + this.formatMappingName());
}
}
protected void initHandlerMethods() {
String[] beanNames = this.obtainApplicationContext().getBeanNamesForType(Object.class);
String[] var2 = beanNames;
int var3 = beanNames.length;
for(int var4 = 0; var4 < var3; ++var4) {
String beanName = var2[var4];
if (!beanName.startsWith("scopedTarget.")) {
Class beanType = null;
try {
beanType = this.obtainApplicationContext().getType(beanName);
} catch (Throwable var8) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Could not resolve type for bean '" + beanName + "'", var8);
}
}
if (beanType != null && this.isHandler(beanType)) {
this.detectHandlerMethods(beanName);
}
}
}
this.handlerMethodsInitialized(this.getHandlerMethods());
}
protected boolean isHandler(Class<?> beanType) {
return AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class);
}
protected void detectHandlerMethods(Object handler) {
Class<?> handlerType = handler instanceof String ? this.obtainApplicationContext().getType((String)handler) : handler.getClass();
if (handlerType != null) {
Class<?> userType = ClassUtils.getUserClass(handlerType);
Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (method) -> {
return this.getMappingForMethod(method, userType);
});
if (this.logger.isTraceEnabled()) {
this.logger.trace(this.formatMappings(userType, methods));
}
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
this.registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
这里可以看出,怎么找方法
- spring是将处理请求的方法的所在类看成处理器了,而不是方法本身,其他场景也有将处理方法作为处理器的,如AbstractHandlerMethodMapping的getHandlerInternal方法返回的处理器就是HandlerMethod类型,在RequestMappingHandlerMappingAdapter中判断是不是支持的Handler也是检查是不是HandlerMethod类型判断的
- 从Handler中获取可以处理请求的方法使用的是HandlerMethodSelector,内部调用// 模板方法,具体实现在RequestMappingHandlerMapping里的,根据有无@RequestMapping找匹配条件,
- // 无则返回null
- // 有则创建RequestMappingInfo类型的匹配条件并返回(也就是@RequestMapping后面的匹配条件,有url、方法类型等信息)
这里可以看出,怎么注册进Map
class MappingRegistry {
private final Map<T, AbstractHandlerMethodMapping.MappingRegistration<T>> registry = new HashMap();
private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap();
private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap();
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
MappingRegistry() {
}
public Map<T, HandlerMethod> getMappings() {
return this.mappingLookup;
}
@Nullable
public CorsConfiguration getCorsConfiguration(HandlerMethod handlerMethod) {
HandlerMethod original = handlerMethod.getResolvedFromHandlerMethod();
return (CorsConfiguration)this.corsLookup.get(original != null ? original : handlerMethod);
}
public void acquireReadLock() {
this.readWriteLock.readLock().lock();
}
public void releaseReadLock() {
this.readWriteLock.readLock().unlock();
}
public void register(T mapping, Object handler, Method method) {
this.readWriteLock.writeLock().lock();
try {
HandlerMethod handlerMethod = AbstractHandlerMethodMapping.this.createHandlerMethod(handler, method);
this.validateMethodMapping(handlerMethod, mapping);
this.mappingLookup.put(mapping, handlerMethod);
CorsConfiguration corsConfig = AbstractHandlerMethodMapping.this.initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
this.corsLookup.put(handlerMethod, corsConfig);
}
this.registry.put(mapping, new AbstractHandlerMethodMapping.MappingRegistration(mapping, handlerMethod));
} finally {
this.readWriteLock.writeLock().unlock();
}
}
private void validateMethodMapping(HandlerMethod handlerMethod, T mapping) {
HandlerMethod existingHandlerMethod = (HandlerMethod)this.mappingLookup.get(mapping);
if (existingHandlerMethod != null && !existingHandlerMethod.equals(handlerMethod)) {
throw new IllegalStateException("Ambiguous mapping. Cannot map '" + handlerMethod.getBean() + "' method \n" + handlerMethod + "\nto " + mapping + ": There is already '" + existingHandlerMethod.getBean() + "' bean method\n" + existingHandlerMethod + " mapped.");
}
}
public void unregister(T mapping) {
this.readWriteLock.writeLock().lock();
try {
AbstractHandlerMethodMapping.MappingRegistration<T> definition = (AbstractHandlerMethodMapping.MappingRegistration)this.registry.remove(mapping);
if (definition != null) {
this.mappingLookup.remove(definition.getMapping());
this.corsLookup.remove(definition.getHandlerMethod());
return;
}
} finally {
this.readWriteLock.writeLock().unlock();
}
}
}
4.2AbstractHandlerMethodMapping之用
通过前面的分析可知这里的主要功能是通过getHandlerInternal方法获取处理器
public Mono<HandlerMethod> getHandlerInternal(ServerWebExchange exchange) {
this.mappingRegistry.acquireReadLock();
try {
HandlerMethod handlerMethod;
try {
handlerMethod = this.lookupHandlerMethod(exchange);
} catch (Exception var8) {
Mono var4 = Mono.error(var8);
return var4;
}
if (handlerMethod != null) {
handlerMethod = handlerMethod.createWithResolvedBean();
}
Mono var3 = Mono.justOrEmpty(handlerMethod);
return var3;
} finally {
this.mappingRegistry.releaseReadLock();
}
}
@Nullable
protected HandlerMethod lookupHandlerMethod(ServerWebExchange exchange) throws Exception {
List<AbstractHandlerMethodMapping<T>.Match> matches = new ArrayList();
this.addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, exchange);
if (!matches.isEmpty()) {
Comparator<AbstractHandlerMethodMapping<T>.Match> comparator = new AbstractHandlerMethodMapping.MatchComparator(this.getMappingComparator(exchange));
matches.sort(comparator);
AbstractHandlerMethodMapping<T>.Match bestMatch = (AbstractHandlerMethodMapping.Match)matches.get(0);
if (matches.size() > 1) {
if (this.logger.isTraceEnabled()) {
this.logger.trace(exchange.getLogPrefix() + matches.size() + " matching mappings: " + matches);
}
if (CorsUtils.isPreFlightRequest(exchange.getRequest())) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
AbstractHandlerMethodMapping<T>.Match secondBestMatch = (AbstractHandlerMethodMapping.Match)matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
RequestPath path = exchange.getRequest().getPath();
throw new IllegalStateException("Ambiguous handler methods mapped for '" + path + "': {" + m1 + ", " + m2 + "}");
}
}
this.handleMatch(bestMatch.mapping, bestMatch.handlerMethod, exchange);
return bestMatch.handlerMethod;
} else {
return this.handleNoMatch(this.mappingRegistry.getMappings().keySet(), exchange);
}
}
小结
二、HandlerAdapter
HandlerMapping通过request找到handler,HandlerAdapter是具体使用Handler来干活的,每个HandlerAdapter封装了一种Handler的具体使用方法,继承体系
- 有5类Adapter,最复杂的是RequestMappingHandlerAdapter的实现,因为该所处理的handler可以是任何方法
- HandlerAdapter接口三个方法,一个用来判断是否支持传入的Handler,一个用来使用Handler处理请求,还有一个就是用来获取资源的Last-Modified值
- HttpRequestHandlerAdapter、SimpleServletHandlerAdapter和SimpleControllerAdapter分别适配HttpRequestHandler、Servlet、Controller类型的Handler,方法很简单,都是调用Handler里固定的方法
1、RequestMappingHandlerAdapter概述
继承AbstractHandlerMethodAdapter类
- 该父类内部三个接口方法分别调用三个模板方法:supportsInternal,getLastModifiedInternal,handleInternal
- 增加了Handler必须是HandlerMethod类型,另外实现了Order接口,可以在配置时设置顺序
RequestMappingHandlerAdapter可以说是SpringMVC最复杂的组件
- supportsInternal()方法直接返回true,只要满足父类判断Handler的条件即可
- getLastModifiedInternal()直接返回-1
- handleInternal()最重要的方法,就是这个方法使用Handler处理请求,
具体步骤
- 备好处理器所需要的参数:方法参数最麻烦
- 使用处理器处理请求:最简单,直接使用反射技术调用处理器执行即可
- 处理返回值,也就是将不同类型的返回值统一处理成ModelAndView类型
参数绑定问题
- 哪些参数需要绑定?
- 参数值的来源?
- 具体的进行绑定的方法?
参数需要根据方法确定,不过这里的方法除了实际处理请求的处理器之外,还有两个方法的参数需要绑定,那就是跟当前处理器相对应加了@ModelAttribute 和@InitBinder 的方法,参数来源有两个
- request中相关的参数:主要包括url中的参数,post过来的参数以及请求头所包含的值
- cookie中的值
- session中的值
- 设置到FlashMap中的参数,主要用于redirect的参数传递
- SessionAttributes传递的参数,该参数主要通过@SessionAttributes注释传递
- 通过@ModelAttribute的方法进行设置的参数
四个注解
- @InitBindner:该注解方法可以初始化Binder,可以注册校验器,如@Valid+校验器进行参数校验场景校验器的设置,还有就是再该方法内部设置编辑器(时间格式化器)
- @ModelAttribute:该注解方法可以将参数、返回值设置到Model中,该注解参数表示需要指定的ArgumentResolver来解析参数,后续介绍
- @ControllerAdvice:上面的两个注解都是在特定的处理器(特定单个Controller)才能使用,如果想让所有的Controller都作用,处理定义一个Controller的基类让所有的XxxController都继承他,将注解方法定义在里面,这样耦合度高,因此SpringMVC提供一个简单解决办法,定义一个类加上@ControllerAdvice,并将注解方法放进去,这样每个XxxController处理请求前都会执行这些方法
- @ReponseBodyAdvice:原理同上,加上了@ResponseBody的方法或者是HttpEntity类型的返回值的XxxController方法,可以被加上@ControllerAdvice注解并且实现了ResponseBodyAdvice接口的类处理,原理是ReturnValueHandler调用的
举栗
2、RequestMappingHandlerAdapter自身结构
2.1.创建RequestMappingHandlerAdapter之器
自身结构并不复杂,就是组件多,RequestMappingHandlerAdapter的创建在afterPropertiesSet方法中实现,主要是初始化两类resolver、@ContrAdvice相关的方法(可以理解为切面),包括方法参数等信息
- argumentResolvers
- initBinderArgumentResolvers
- returnValueHandlers
- @ControllerAdvice注解相关的modelAttributeAdviceCache
- @ControllerAdvice注解相关的initBinderAdviceCache
- @ControllerAdvice注解相关的responseBodyAdvice
@Override
public void afterPropertiesSet() {
initControllerAdviceCache();
if (this.argumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.initBinderArgumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.returnValueHandlers == null) {
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
}
private void initControllerAdviceCache() {
if (getApplicationContext() == null) {
return;
}
List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
List<Object> requestResponseBodyAdviceBeans = new ArrayList<>();
for (ControllerAdviceBean adviceBean : adviceBeans) {
Class<?> beanType = adviceBean.getBeanType();
if (beanType == null) {
throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
}
Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS);
if (!attrMethods.isEmpty()) {
this.modelAttributeAdviceCache.put(adviceBean, attrMethods);
}
Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS);
if (!binderMethods.isEmpty()) {
this.initBinderAdviceCache.put(adviceBean, binderMethods);
}
if (RequestBodyAdvice.class.isAssignableFrom(beanType) || ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
requestResponseBodyAdviceBeans.add(adviceBean);
}
}
if (!requestResponseBodyAdviceBeans.isEmpty()) {
this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
}
}
public static final MethodFilter MODEL_ATTRIBUTE_METHODS = method ->
(!AnnotatedElementUtils.hasAnnotation(method, RequestMapping.class) &&
AnnotatedElementUtils.hasAnnotation(method, ModelAttribute.class));
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
resolvers.add(new RequestParamMapMethodArgumentResolver());
resolvers.add(new PathVariableMethodArgumentResolver());
resolvers.add(new PathVariableMapMethodArgumentResolver());
resolvers.add(new MatrixVariableMethodArgumentResolver());
resolvers.add(new MatrixVariableMapMethodArgumentResolver());
resolvers.add(new ServletModelAttributeMethodProcessor(false));
resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
resolvers.add(new RequestHeaderMapMethodArgumentResolver());
resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new SessionAttributeMethodArgumentResolver());
resolvers.add(new RequestAttributeMethodArgumentResolver());
resolvers.add(new ServletRequestMethodArgumentResolver());
resolvers.add(new ServletResponseMethodArgumentResolver());
resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RedirectAttributesMethodArgumentResolver());
resolvers.add(new ModelMethodProcessor());
resolvers.add(new MapMethodProcessor());
resolvers.add(new ErrorsMethodArgumentResolver());
resolvers.add(new SessionStatusMethodArgumentResolver());
resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
if (getCustomArgumentResolvers() != null) {
resolvers.addAll(getCustomArgumentResolvers());
}
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
resolvers.add(new ServletModelAttributeMethodProcessor(true));
return resolvers;
}
2.2RequestMappingHandlerAdapter之用
概述
- 处理请求的入口方法是handleInternal(),handleInternal()干了两件事
- 检查request:可以设置对请求类型的限制等,默认不检查。
- 执行处理请求
- // 1. 首先创建ServletWebRequest对象,// 在ArgumentResolver解析参数时候的request就是这个对象,处理器需要HttpServletRequest,这是ArgumentResolver就会转换设置
- // 2. 初始化3个变量
- // 3. 创建真正的执行处理请求的方法对象,在此对象内将参数解析组件、返回值处理组件 等组件设置进去
- // 4. 创建ModelAndViewContainer容器,处理传递的参数Model和View,执行请求,最后处理ModelAndView
初始化的三个变量
- WebDataBinderfactory:处理@InitBinder注解的方法
- ModelFactory:处理Model的,主要是处理器具体处理之前对Model初始化,处理完成之后对Model初始化将Model对应的参数更新到SessionAttributes中
-
处理请求之前:1、将原来加了@SessionAttributes注解对应的的值设置到Model。2、执行加了@ModelAttribute注解的方法,将值设置到Model(参数、返回值等)。3、加了@ModelAttribute的参数设置 -
处理完请求之后做后置处理,在getModelAndView方法中处理的,一共做了3件事,1、调用ModelFactory的updateModel更新Model(包括设置SessionAttributes 合 給Model设置BindingResult。2、根据mavContainer创建ModelAndView。3、如果mavCotainer里的model是RedirectAttrobutes类型,则将其值设置到FlashMap - ServletInvocableHandlerMethod:继承HandlerMethod,可以直接执行,实际请求,参数绑定,请求处理,返回值处理都在这边完成
请求处理入口方法是handleInternal()
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
checkRequest(request);
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}
检查请求
@Nullable
private Set<String> supportedMethods;
protected final void checkRequest(HttpServletRequest request) throws ServletException {
String method = request.getMethod();
if (this.supportedMethods != null && !this.supportedMethods.contains(method)) {
throw new HttpRequestMethodNotSupportedException(method, this.supportedMethods);
}
if (this.requireSession && request.getSession(false) == null) {
throw new HttpSessionRequiredException("Pre-existing session required but none found");
}
}
执行请求处理
-
首先创建ServletWebRequest对象 // 在ArgumentResolver解析参数时候的request就是这个对象,处理器需要HttpServletRequest,这是ArgumentResolver就会转换设置 -
初始化3个变量 // 2.1用于参数跟String之间的类型转换,ModelFactory 刷新也会用到它:// 将符合条件注释了@InitBinder的方法找出来,这些方法主要用于处理参数 // 2.2主要是处理Model的,主要包含两个功能,1. 在处理器具体处理之前对Model初始化, 2. 再处理完成请求后对Model参数更新 - 将原来的SessionAttributes的值设置的Model - 执行相应注释@ModelAttribute的方法并将值设置到Model - 处理器注释了了@ModelAttribute的参数同时在SessionAttributes中设置了,而且在mvcCobtainer中没有,则将全部的SessionAttributes中查找并设置进去 -
创建invocableMethod 真正的执行者:继承HandlerMethod,并且可以直接执行,实际请求的处理就是通过它执行的,参数绑定、处理请求、返回值都是这里完成 -
将参数解析组件、返回值处理组件 等组件设置进去:也就是子类实现完成之后的组件,对应之前创建之器声明的哪些保存在成员变量的组件 -
新建mavContainer:保存Model和View,请求参数传递进去
执行
@Nullable
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 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 mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(result, !traceOn);
return "Resume with async result [" + formatted + "]";
});
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
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);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs);
if (this.logger.isTraceEnabled()) {
this.logger.trace("Arguments: " + Arrays.toString(args));
}
return this.doInvoke(args);
}
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
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];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] == null) {
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 var10) {
if (this.logger.isDebugEnabled()) {
String exMsg = var10.getMessage();
if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
this.logger.debug(formatArgumentError(parameter, exMsg));
}
}
throw var10;
}
}
}
return args;
}
}
@Nullable
protected Object doInvoke(Object... args) throws Exception {
ReflectionUtils.makeAccessible(this.getBridgedMethod());
try {
return this.getBridgedMethod().invoke(this.getBean(), args);
} catch (IllegalArgumentException var4) {
this.assertTargetBean(this.getBridgedMethod(), this.getBean(), args);
String text = var4.getMessage() != null ? var4.getMessage() : "Illegal argument";
throw new IllegalStateException(this.formatInvokeError(text, args), var4);
} catch (InvocationTargetException var5) {
Throwable targetException = var5.getTargetException();
if (targetException instanceof RuntimeException) {
throw (RuntimeException)targetException;
} else if (targetException instanceof Error) {
throw (Error)targetException;
} else if (targetException instanceof Exception) {
throw (Exception)targetException;
} else {
throw new IllegalStateException(this.formatInvokeError("Invocation failure", args), targetException);
}
}
}
@Nullable
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
modelFactory.updateModel(webRequest, mavContainer);
if (mavContainer.isRequestHandled()) {
return null;
}
ModelMap model = mavContainer.getModel();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (request != null) {
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
}
return mav;
}
注解@SessionAttributes概述
- 主要用在处理器类上,用于在多个请求之间传递参数,类似于Session的Attribute,但是也不一样,前者只适用暂时的传递,而不是长期的保存,长期应该适用Sesssion的setAttribute()保存
- 主要用法:1、设置了该注解的参数在视图中通过request、session的的getAttribute()获取,2、在后面请求返回的视图中通过session的getAttribute或者Model获取,3、自动将参数设置到后面请求处理器的Model或者@ModelAttribute注解的参数
- 设置方法:1、需要满足在@SessionAttribute注解设置参数名、类型,2、在处理器中将参数设置到Model,之后会自动的有ModelFactory完成更新,使用完毕之后可以调用SessionStatus.setComplete()清除,这对已经存入session的参数。
小结
- SessionAttributesHandler:用来处理@SessionAttributes注释的参数,做一些宏观的事情,如那个Handler可以缓存哪些参数,某个参数再当前的SessionAttribues中是否存在
- SessionAttributeStore:并不是保存数据的容器,而是工具,单个参数的处理操作,可以临时当做缓存处理,容器默认为Session,默认实现DefaultSessionAttributeStore使用ServletWebRequest将参数设置到Session中,具体是在ModelFactory使用
public class SessionAttributesHandler {
private final Set<String> attributeNames = new HashSet();
private final Set<Class<?>> attributeTypes = new HashSet();
private final Set<String> knownAttributeNames = Collections.newSetFromMap(new ConcurrentHashMap(4));
private final SessionAttributeStore sessionAttributeStore;
public SessionAttributesHandler(Class<?> handlerType, SessionAttributeStore sessionAttributeStore) {
Assert.notNull(sessionAttributeStore, "SessionAttributeStore may not be null");
this.sessionAttributeStore = sessionAttributeStore;
SessionAttributes ann = (SessionAttributes)AnnotatedElementUtils.findMergedAnnotation(handlerType, SessionAttributes.class);
if (ann != null) {
Collections.addAll(this.attributeNames, ann.names());
Collections.addAll(this.attributeTypes, ann.types());
}
this.knownAttributeNames.addAll(this.attributeNames);
}
public boolean hasSessionAttributes() {
return !this.attributeNames.isEmpty() || !this.attributeTypes.isEmpty();
}
public boolean isHandlerSessionAttribute(String attributeName, Class<?> attributeType) {
Assert.notNull(attributeName, "Attribute name must not be null");
if (!this.attributeNames.contains(attributeName) && !this.attributeTypes.contains(attributeType)) {
return false;
} else {
this.knownAttributeNames.add(attributeName);
return true;
}
}
public void storeAttributes(WebRequest request, Map<String, ?> attributes) {
attributes.forEach((name, value) -> {
if (value != null && this.isHandlerSessionAttribute(name, value.getClass())) {
this.sessionAttributeStore.storeAttribute(request, name, value);
}
});
}
public Map<String, Object> retrieveAttributes(WebRequest request) {
Map<String, Object> attributes = new HashMap();
Iterator var3 = this.knownAttributeNames.iterator();
while(var3.hasNext()) {
String name = (String)var3.next();
Object value = this.sessionAttributeStore.retrieveAttribute(request, name);
if (value != null) {
attributes.put(name, value);
}
}
return attributes;
}
public void cleanupAttributes(WebRequest request) {
Iterator var2 = this.knownAttributeNames.iterator();
while(var2.hasNext()) {
String attributeName = (String)var2.next();
this.sessionAttributeStore.cleanupAttribute(request, attributeName);
}
}
@Nullable
Object retrieveAttribute(WebRequest request, String attributeName) {
return this.sessionAttributeStore.retrieveAttribute(request, attributeName);
}
}
2.3ModelAndViewContainer
- ModelAndViewContainer:承担整个过程中数据传递工作,除了保存Model和View 外还有一些别的功能,
- 内有属性defaultModel:主要用于封装默认Model数据,就是默认使用Model和ModelAndView时候,ArgumentResolver会传入defaultModel,继承了ModelMap实现了Model接口
- 内有属性redirectModel:主要用于封装重定向之后Model的数据
- 内有属性view:可以是逻辑、实际视图
- 内有属性requestHandler:主要是标志请求是否处理完返回response,是就不在往下处理,直接返回。有@ResposeBody注解的方法或者返回值为Httpentity的都会将该属性设置为true
- …
2.4InvocableHandlerMethod执行者
先看父类HandlerMethod
- 封装Handler和具体处理请求的Method,分别对应bean和method属性,除了这两个还有其他三个属性
- beanFactory:新建HandlerMethod时候传入的Handler是String类型,需要获取Bean
- bridgeMethod:如果Method是bridgeMethod则设置为原有方法
- parameters:请求方法的参数集合,具体元素为MethodParameter类型(内部类),一个对象代表一个方法的请求参数
在父类的基础上
- 增加了方法执行功能
- 相应的增加了参数解析、处理返回值等
- 在父类的基础上加了调用的功能,可以直接调用内部属性method对应的bridgeMethod
- 增加了属性dataBinderFactory(参数解析器使用)、argumentResolvers(解析参数)、paramaterNameDiscover(获取参数名)
MethodParameter 请求方法参数类的成员属性
另外封装参数的内部类是ReturnValueMethodParameter,主要负责方法返回的参数
- 继承HandlerMethodParameter
- 使用的方法都是bridgeMethod,桥方法,为了处理父类泛型的问题,不涉及泛型就是原方法
- 返回值使用的parameterIndex都是-1
子类ServletInvocableHandlerMethod,在父类的基础上增加了3个功能
- 对@ResponseStatus的支持
- 对返回值的处理:HandlerMethodReturnValueHandler类型的成员变量
- 对一部处理结果的处理
2.5HandlerMethodArgumentResolver
概述
- 是用来处理器解析参数的,主要用在上面的InvocableHandlerMethod中,每个Resolver对应一种类型的参数,也就是创建RequestHandelrAdapter时候声明的那些待子类实现的XxxResolver组件的父接口
- 每个实现类对应一种类型的参数,命名大致两种
- XxxMethodArgumentResolver
- XxxMethodProcess:另外包含处理响应类型的返回值
- HandlerMethodArgumentResolverComposite内部可以防多个XxxResolver
解析器介绍
1、解析Model类型参数的ModelMethodProcess解析器
既可以解析参数,也可哟处理返回值
public class ModelMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {
public ModelMethodProcessor() {
}
public boolean supportsParameter(MethodParameter parameter) {
return Model.class.isAssignableFrom(parameter.getParameterType());
}
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
Assert.state(mavContainer != null, "ModelAndViewContainer is required for model exposure");
return mavContainer.getModel();
}
public boolean supportsReturnType(MethodParameter returnType) {
return Model.class.isAssignableFrom(returnType.getParameterType());
}
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
if (returnValue != null) {
if (returnValue instanceof Model) {
mavContainer.addAllAttributes(((Model)returnValue).asMap());
} else {
throw new UnsupportedOperationException("Unexpected return type: " + returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
}
}
}
}
2、解析@PathVariable参数类型的PathVariableMethodArgumentResolver解析器
先看父类AbstractNamedValueMethodArgumentResolver
@Nullable
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
AbstractNamedValueMethodArgumentResolver.NamedValueInfo namedValueInfo = this.getNamedValueInfo(parameter);
MethodParameter nestedParameter = parameter.nestedIfOptional();
Object resolvedName = this.resolveStringValue(namedValueInfo.name);
if (resolvedName == null) {
throw new IllegalArgumentException("Specified name must not resolve to null: [" + namedValueInfo.name + "]");
} else {
Object arg = this.resolveName(resolvedName.toString(), nestedParameter, webRequest);
if (arg == null) {
if (namedValueInfo.defaultValue != null) {
arg = this.resolveStringValue(namedValueInfo.defaultValue);
} else if (namedValueInfo.required && !nestedParameter.isOptional()) {
this.handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
}
arg = this.handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
} else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
arg = this.resolveStringValue(namedValueInfo.defaultValue);
}
if (binderFactory != null) {
WebDataBinder binder = binderFactory.createBinder(webRequest, (Object)null, namedValueInfo.name);
try {
arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
} catch (ConversionNotSupportedException var11) {
throw new MethodArgumentConversionNotSupportedException(arg, var11.getRequiredType(), namedValueInfo.name, parameter, var11.getCause());
} catch (TypeMismatchException var12) {
throw new MethodArgumentTypeMismatchException(arg, var12.getRequiredType(), namedValueInfo.name, parameter, var12.getCause());
}
}
this.handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
return arg;
}
}
首先根据参数类型会去到NameValueInfo,然后将它传入模板方法resolveName由子类具体实现,最后对解析的结果处理,内部类NameValueInfo
protected static class NamedValueInfo {
private final String name;
private final boolean required;
@Nullable
private final String defaultValue;
public NamedValueInfo(String name, boolean required, @Nullable String defaultValue) {
this.name = name;
this.required = required;
this.defaultValue = defaultValue;
}
}
PathVariableMethodArgumentResolver类
public class PathVariableMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver implements UriComponentsContributor {
private static final TypeDescriptor STRING_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(String.class);
public PathVariableMethodArgumentResolver() {
}
public boolean supportsParameter(MethodParameter parameter) {
if (!parameter.hasParameterAnnotation(PathVariable.class)) {
return false;
} else if (!Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
return true;
} else {
PathVariable pathVariable = (PathVariable)parameter.getParameterAnnotation(PathVariable.class);
return pathVariable != null && StringUtils.hasText(pathVariable.value());
}
}
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
PathVariable ann = (PathVariable)parameter.getParameterAnnotation(PathVariable.class);
Assert.state(ann != null, "No PathVariable annotation");
return new PathVariableMethodArgumentResolver.PathVariableNamedValueInfo(ann);
}
@Nullable
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
Map<String, String> uriTemplateVars = (Map)request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, 0);
return uriTemplateVars != null ? uriTemplateVars.get(name) : null;
}
protected void handleMissingValue(String name, MethodParameter parameter) throws ServletRequestBindingException {
throw new MissingPathVariableException(name, parameter);
}
protected void handleResolvedValue(@Nullable Object arg, String name, MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest request) {
String key = View.PATH_VARIABLES;
int scope = 0;
Map<String, Object> pathVars = (Map)request.getAttribute(key, scope);
if (pathVars == null) {
pathVars = new HashMap();
request.setAttribute(key, pathVars, scope);
}
((Map)pathVars).put(name, arg);
}
public void contributeMethodArgument(MethodParameter parameter, Object value, UriComponentsBuilder builder, Map<String, Object> uriVariables, ConversionService conversionService) {
if (!Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
PathVariable ann = (PathVariable)parameter.getParameterAnnotation(PathVariable.class);
String name = ann != null && StringUtils.hasLength(ann.value()) ? ann.value() : parameter.getParameterName();
String formatted = this.formatUriValue(conversionService, new TypeDescriptor(parameter.nestedIfOptional()), value);
uriVariables.put(name, formatted);
}
}
@Nullable
protected String formatUriValue(@Nullable ConversionService cs, @Nullable TypeDescriptor sourceType, Object value) {
if (value instanceof String) {
return (String)value;
} else {
return cs != null ? (String)cs.convert(value, sourceType, STRING_TYPE_DESCRIPTOR) : value.toString();
}
}
private static class PathVariableNamedValueInfo extends NamedValueInfo {
public PathVariableNamedValueInfo(PathVariable annotation) {
super(annotation.name(), annotation.required(), "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n");
}
}
}
涉及方法
- getNameValueInfo:通过参数类型获得NamedValueInfo,PathVariableNamedValueInfo 根据构造方法使用@PAthVariable注解的value创建了PathVariableNamedValueInfo
- resolveName:具体解析参数,模板方法,可以看出PathVariableMethodArgumentResolver 实现直接从request中获取的HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE属性的值,这个是是在RequestMappingInfoHandlerMapping中的handlerMathch中设置的,也就是在HandlerMapping中根据loouupPath找到处理请求的处理器之后设置的
- resolveDefaultValue:设置默认值
- handlerMissingValue:参数必须存在并且没有默认值,就会调用该模板方法
- handlerResolverValue:处理解析出的参数值的模板方法,PathVariableMethodArgumentResolver 实现将解析的参数设置到request中
2.6HandlerMethodReturnValueHandler
作用在ServletInvocableHandlerMethod中,作用是处理处理器执行后的返回值,主要由三个功能
- 将对应的参数添加到Model
- 设置view
- 如果请求已经处理完成ModelAndViewContainer的requestHandled为true
原理和上述类似,主要一个包括判断支持、一个用于具体处理返回值,有很多实现子类
3、总结
- HandlerAdapter主要的四个实现类,RequestMappingHandlerAdapter较为复杂,其他简单,应该是SpringMVC最复杂的组件
- 主要流程三步骤:将诶西参数、执行请求、处理返回值
- 解析参数的过程参数来源有:Model、request,前者通过FlashMapManager和ModelFactory管理
- 执行请求是HandlerMethod的子类ServletInvocableHandlerMethod,实际执行的时InvocableHandlerMethod
- 返回值有HandlerMethodReturnValueHandler处理,不同的返回值对应不同的实现子类
三、ViewResolver
主要作用是根据视图名和Locale解析出视图,解析过程主要做两件事
主要的实现类
- ContentNegotiatingViewResolver主要作用是在别的解析器解析的结果加上Content-Type,对视图的解析并不是自己完成的,而是使用封装的ViewResolver来进行的,整个过程:首先遍历所封装的ViewResolver具体解析视图,可能会有多个结果,然后在使用request获取的Content-Type,最后对这两个结果进行匹配查找最优的视图
- AbstractCachingViewResolver系列:提供了统一的缓存功能,当视图解析过一次就被缓存起来,直到缓存被删除前视图的解析都会从缓存中获取
四、RequestToViewTranslator
五、HandlerExceptionResolver
主要作用是解析请求处理过程中产生的异常
- AbstractHandlerExceptionResolver:模板类和子类ExceptionHandlerExceptionResolver一起完成@ExceptionHandler注解方法进行异常解析
- DefaultHandlerExceptionResolver:按照不同类型分别对异常解析
- ResponseStatusExceptionResolver:解析有@ResponseStatus注解类型的异常
- SimpleMappingExceptionResolver:通过配置的异常和view的对应关系来解析异常
解析过程主要包括:给ModelAndView设置相应内容、设置response的相关属性,当然可以有一些辅助功能如记录日志
@Override
@Nullable
public ModelAndView resolveException(
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
if (shouldApplyTo(request, handler)) {
prepareResponse(ex, response);
ModelAndView result = doResolveException(request, response, handler, ex);
if (result != null) {
if (logger.isDebugEnabled() && (this.warnLogger == null || !this.warnLogger.isWarnEnabled())) {
logger.debug("Resolved [" + ex + "]" + (result.isEmpty() ? "" : " to " + result));
}
logException(ex, request);
}
return result;
}
else {
return null;
}
}
六、MultipartResolver
用于处理上传请求,有两个实现类,
1、StandardServletMultipartResolver
配置的参数
七、SpringMVC原理总结
1、原理总结
2、跟踪一个处理流程
SpringMVC的处理过程
下面具体分析
0、SpringMVC的创建
1、发送请求(Servlet容器)
2、ServletRequest转为HttpServletrequest(HttpSetvlet)
3、请求初步经service()处理(FrameworkServlet、HttpServlet之间)
4、请求汇总到processRequest(FrameworkServlet)
5、doService调用doDispatch内部调用getHandler(DispatchServlet)
6、根据请求寻找Handler及其处理方法(RequestMappingHandlerMapping)
7、找到处理方法之后将请求参数的设置request里面(RequestMappingInfoHandlerMapping)
8、根据Handler找HandlerAdapter之后处理请求(DispatchServlet)
9、HandlerAdapter调用Handler处理请求(RequestMappingHandlerAdapter)
- HandlerAdapter的handler()方法是模板方法,进而调用子类RequestMappingHandlerAdapter的getHandlerInternal()方法
10、执行请求对应方法之前(RequestMappingHandlerAdapter)
11、类似切面的方法初始化Model数据(ModelFactory)
12、执行请求对应方法(InvocableHandlerMethod、处理器FollowMeController)
13、视图View的处理(ServletInvocableHandlerMethod、ViewNameMethodReturnValueHandler)
14、ModelAndView的处理(RequestMappingHandlerAdapter)
15、准备View的检查及其后续拦截器(DispatchServlet)
16、准备View的渲染(DispatchServlet)
17、请求处理完成(RedirectView、DispatchServlet、FrameworkServlet)
|