spring framework 5.1.8
问题一:HandlerMapping是什么?
objects that define a mapping between requests and handler objects.
从HandlerMapping
接口的注释来看,它是一个关联请求与处理器的对象。它的作用是根据request找到相应的处理器Handler和Interceptors。
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
其中HandlerExecutionChain
对象主要由Handler
和HandlerInterceptor[] interceptors
组成。
可以类比我们在springMVC
中的用法:
HandlerMapping 可以根据url请求找到最适合的controller,继而处理程序。
问题二: HandlerMapping的常用实现
- AbstractHandlerMapping
- AbstractUrlHandlerMapping
- SimpleUrlHandlerMapping
- BeanNameUrlHandlerMapping
- RequestMappingHandlerMapping
AbstractHandlerMapping部分源码分析
...
private final List<Object> interceptors = new ArrayList<>();
private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<>();
...
//初始化interceptors
@Override
protected void initApplicationContext() throws BeansException {
//这个方法没有被实现 是空方法
extendInterceptors(this.interceptors);
detectMappedInterceptors(this.adaptedInterceptors);
initInterceptors();
}
//检测Interceptors
//将当前上下文以及当前上下文的父级上下文中类型是MappedInterceptor的Bean 添加到mappedInterceptors集合
protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
//这种从spring上下文中检索Bean的方法很常用,在初始化HandlerMappings和HandlerAdapters中也多次用到
mappedInterceptors.addAll(
BeanFactoryUtils.beansOfTypeIncludingAncestors(
obtainApplicationContext(), MappedInterceptor.class, true, false).values());
}
//初始化Interceptors
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");
}
//将适配后的interceptor添加到adaptedInterceptors集合中
this.adaptedInterceptors.add(adaptInterceptor(interceptor));
}
}
}
protected HandlerInterceptor adaptInterceptor(Object interceptor) {
if (interceptor instanceof HandlerInterceptor) {
return (HandlerInterceptor) interceptor;
}
else if (interceptor instanceof WebRequestInterceptor) {
return new WebRequestHandlerInterceptorAdapter((WebRequestInterceptor) interceptor);
}
else {
throw new IllegalArgumentException("Interceptor type not supported: " + interceptor.getClass().getName());
}
}
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//根据request找到适配的Handler ①
Object handler = getHandlerInternal(request);
//如果handler,则选用默认的Handler
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
//如果handler是个字符串,那么从IOC容器中根据名称找到对应的Bean
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
//②
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
}
else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
logger.debug("Mapped to " + executionChain.getHandler());
}
//如果是跨域请求
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
//① 其子类AbstractURLHandlerMapping实现了该方法
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
//从request中取出请求路径
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
//①① 从hanlderMap关系中 获取handler
Object handler = lookupHandler(lookupPath, request);
if (handler == null) {
Object rawHandler = null;
//如果请求路径=/
if ("/".equals(lookupPath)) {
rawHandler = getRootHandler();
}
//如果rawHandler为空,那么使用默认Handler
if (rawHandler == null) {
rawHandler = getDefaultHandler();
}
if (rawHandler != null) {
//如果handler是字符串,则在IOC容器中找到Bean
if (rawHandler instanceof String) {
String handlerName = (String) rawHandler;
rawHandler = obtainApplicationContext().getBean(handlerName);
}
//验证handler
validateHandler(rawHandler, request);
//根据handler构造handlerExecutionChain,其中放入数个拦截器
handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
}
}
return handler;
}
//①① 其中方法中的handlerMap数据是怎么来的后面会说。
//Map<String, Object> handlerMap = new LinkedHashMap<>(),存储的数据类似于key="/say",value=DemoController
@Nullable
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
//从path-handler关联关系中取出handler
Object handler = this.handlerMap.get(urlPath);
if (handler != null) {
//如果handler是字符串,那么获取到其Bean
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
//校验该handler
validateHandler(handler, request);
//根据handler生成HandlerExecutuonChain,并往其中加入PathExposingHandlerInterceptor/UriTemplateVariablesHandlerInterceptor 这两个拦截器
return buildPathExposingHandler(handler, urlPath, urlPath, null);
}
//如果路径是采用的正则表达式匹配,那么这里会寻找一个最合适的url
List<String> matchingPatterns = new ArrayList<>();
for (String registeredPattern : this.handlerMap.keySet()) {
if (getPathMatcher().match(registeredPattern, urlPath)) {
matchingPatterns.add(registeredPattern);
}
else if (useTrailingSlashMatch()) {
if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
matchingPatterns.add(registeredPattern + "/");
}
}
}
String bestMatch = null;
Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
if (!matchingPatterns.isEmpty()) {
matchingPatterns.sort(patternComparator);
if (logger.isTraceEnabled() && matchingPatterns.size() > 1) {
logger.trace("Matching patterns " + matchingPatterns);
}
bestMatch = matchingPatterns.get(0);
}
//根据找到的最合适的url 找到Handler,随后跟上面一样 构造HandlerExecutionChain,并添加拦截器
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 + "]");
}
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);
// There might be multiple 'best patterns', let's make sure we have the correct URI template variables
// for all of them
Map<String, String> uriTemplateVariables = new LinkedHashMap<>();
for (String matchingPattern : matchingPatterns) {
if (patternComparator.compare(bestMatch, matchingPattern) == 0) {
Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
uriTemplateVariables.putAll(decodedVars);
}
}
if (logger.isTraceEnabled() && uriTemplateVariables.size() > 0) {
logger.trace("URI variables " + uriTemplateVariables);
}
return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
}
// No handler found...
return null;
}
//② 生成handler执行链
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;
}
MappedInterceptor与HandlerInterceptor的区别?
MappedInterceptor:一个包括includePatterns和excludePatterns字符串集合并带有HandlerInterceptor的类。 很明显,就是对于某些地址做特殊包括和排除的拦截器。
AbstractUrlHandlerMapping部分源码分析
AbstractHandlerMapping作为抽象类,很多地方法实现都放在了AbstractUrlHandlerMapping类中,如getHandlerInternal()等,由于在上面说过了,这里不再赘述。至于handlerMap的值是怎么来的,在初始化handlerMapping时再介绍
SimpleUrlHandlerMapping部分源码分析
...
private final Map<String, Object> urlMap = new LinkedHashMap<>();
...
@Override
public void initApplicationContext() throws BeansException {
//调用super,其实就是AbstractHandlerMapping#initApplicationContext()
super.initApplicationContext();
//① this.urlMap怎么被赋值的,我们在后面说
registerHandlers(this.urlMap);
}
protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
if (urlMap.isEmpty()) {
logger.trace("No patterns in " + formatMappingName());
}
else {
urlMap.forEach((url, handler) -> {
// Prepend with slash if not already present.
if (!url.startsWith("/")) {
url = "/" + url;
}
// Remove whitespace from handler bean name.
if (handler instanceof String) {
handler = ((String) handler).trim();
}
//②
registerHandler(url, handler);
});
if (logger.isDebugEnabled()) {
List<String> patterns = new ArrayList<>();
if (getRootHandler() != null) {
patterns.add("/");
}
if (getDefaultHandler() != null) {
patterns.add("/**");
}
patterns.addAll(getHandlerMap().keySet());
logger.debug("Patterns " + patterns + " in " + formatMappingName());
}
}
}
//② 调用了父类AbstractURLHandlerMapping
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;
// Eagerly resolve handler if referencing singleton via name.
//如果handler不是懒加载且是字符串类型
if (!this.lazyInitHandlers && handler instanceof String) {
String handlerName = (String) handler;
ApplicationContext applicationContext = obtainApplicationContext();
if (applicationContext.isSingleton(handlerName)) {
//如果handlerName是单例Bean名称,则获取Bean
resolvedHandler = applicationContext.getBean(handlerName);
}
}
//根据urlPath 从handlerMap中获取handler
Object mappedHandler = this.handlerMap.get(urlPath);
if (mappedHandler != null) {
//如果通过url解析出的handler和通过urlMap注册来的handler不一致 则抛出异常
if (mappedHandler != resolvedHandler) {
throw new IllegalStateException(
"Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
"]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
}
}
//如果mappedHandler为空
else {
if (urlPath.equals("/")) {
if (logger.isTraceEnabled()) {
logger.trace("Root mapping to " + getHandlerDescription(handler));
}
//如果请求路径是/ 将resolveHandler设置为rootHandler
setRootHandler(resolvedHandler);
}
else if (urlPath.equals("/*")) {
if (logger.isTraceEnabled()) {
logger.trace("Default mapping to " + getHandlerDescription(handler));
}
//如果请求路径是/ 将resolveHandler设置为defaultHandler
setDefaultHandler(resolvedHandler);
}
else {
//将url与handler加入到handlerMap中
this.handlerMap.put(urlPath, resolvedHandler);
if (logger.isTraceEnabled()) {
logger.trace("Mapped [" + urlPath + "] onto " + getHandlerDescription(handler));
}
}
}
}
handlerMap赋值是在AbstractUrlHandlerMapping#registerHandler
BeanNameUrlHandlerMapping 部分源码分析
由于BeanNameUrlHandlerMapping
继承自AbstractDetectingUrlHandlerMapping
,所以initApplicationContext在父类
@Override
public void initApplicationContext() throws ApplicationContextException {
//该方法在AbstractHandlerMapping
super.initApplicationContext();
detectHandlers();
}
//AbstractDetectingUrlHandlerMapping类
protected void detectHandlers() throws BeansException {
//获取实际使用的ApplicationContext
ApplicationContext applicationContext = obtainApplicationContext();
//detectHandlersInAncestorContexts 表示是否从祖先上下文中检索handler 默认是false
String[] beanNames = (this.detectHandlersInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) :
//从上下文中取出type=Object的Bean ?拿出来的beanName是什么
applicationContext.getBeanNamesForType(Object.class));
// 接受任何可以确定url的bean名称.
for (String beanName : beanNames) {
//这个方法在BeanNameUrlHandlerMapping中
String[] urls = determineUrlsForHandler(beanName);
if (!ObjectUtils.isEmpty(urls)) {
//AbstractUrlhandlerMapping#registerHandler
registerHandler(urls, beanName);
}
}
if ((logger.isDebugEnabled() && !getHandlerMap().isEmpty()) || logger.isTraceEnabled()) {
logger.debug("Detected " + getHandlerMap().size() + " mappings in " + formatMappingName());
}
}
怎么查看系统默认的handlerMapping?
在Dispacther.properties文件中可以查看,如下:
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
handlerMapping的初始化
//DispatcherServlet
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
//是否检索所有Handlermappings 默认是true
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
//从当前上下文以及祖先上下文中查找类型是HandlerMapping的Bean
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
//由于HandlerMapping的实现类都实现了Ordered,所以诸多HandlerMapping之间是有顺序的,这里是进行排序
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
try {
//如果不是检索所有上下文,则从当前上下文中检索handlerMapping
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
if (this.handlerMappings == null) {
//如果前面都获取不到HandlerMappings,则采用默认的HandlerMapping,即DispatchServlet.properties中预设的
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
SimpleUrlHandlerMapping中urlMap是如何被赋值的&HandlerMapping是什么时候被加载进容器的?
首先跟HandlerMapping有关的即是Web项目,springmvc框架有@Enable机制,比如@EnbaleWebMvc
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}
会执行DelegatingWebMvcConfiguration
,而HanlderMapping注入为Bean是在其父类WebMvcConfigurationSupport
@Bean
public BeanNameUrlHandlerMapping beanNameHandlerMapping() {
BeanNameUrlHandlerMapping mapping = new BeanNameUrlHandlerMapping();
mapping.setOrder(2);
mapping.setInterceptors(getInterceptors());
mapping.setCorsConfigurations(getCorsConfigurations());
return mapping;
}
---
@Bean
@Nullable
public HandlerMapping resourceHandlerMapping() {
Assert.state(this.applicationContext != null, "No ApplicationContext set");
Assert.state(this.servletContext != null, "No ServletContext set");
ResourceHandlerRegistry registry = new ResourceHandlerRegistry(this.applicationContext,
this.servletContext, mvcContentNegotiationManager(), mvcUrlPathHelper());
addResourceHandlers(registry);
AbstractHandlerMapping handlerMapping = registry.getHandlerMapping();
if (handlerMapping == null) {
return null;
}
handlerMapping.setPathMatcher(mvcPathMatcher());
handlerMapping.setUrlPathHelper(mvcUrlPathHelper());
handlerMapping.setInterceptors(getInterceptors());
handlerMapping.setCorsConfigurations(getCorsConfigurations());
return handlerMapping;
}
//ResourceHandlerRegistry#getHandlerMapping
@Nullable
protected AbstractHandlerMapping getHandlerMapping() {
if (this.registrations.isEmpty()) {
return null;
}
Map<String, HttpRequestHandler> urlMap = new LinkedHashMap<>();
for (ResourceHandlerRegistration registration : this.registrations) {
for (String pathPattern : registration.getPathPatterns()) {
ResourceHttpRequestHandler handler = registration.getRequestHandler();
if (this.pathHelper != null) {
handler.setUrlPathHelper(this.pathHelper);
}
if (this.contentNegotiationManager != null) {
handler.setContentNegotiationManager(this.contentNegotiationManager);
}
handler.setServletContext(this.servletContext);
handler.setApplicationContext(this.applicationContext);
try {
handler.afterPropertiesSet();
}
catch (Throwable ex) {
throw new BeanInitializationException("Failed to init ResourceHttpRequestHandler", ex);
}
urlMap.put(pathPattern, handler);
}
}
SimpleUrlHandlerMapping handlerMapping = new SimpleUrlHandlerMapping();
handlerMapping.setOrder(this.order);
//这里就从外部将urlMap设置到SimpleUrlHandlerMapping中去
handlerMapping.setUrlMap(urlMap);
return handlerMapping;
}
...
//等等 在WebMvcConfigurationSupport类中 构成Bean的HandlerMapping还有多处
其次 作为SpringBoot项目,其AutoConfiguration也会注入HandlerMapping。
# spring.factories
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
...
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
...
//WebMvcAutoConfiguration
...
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext) {
return new WelcomePageHandlerMapping(new TemplateAvailabilityProviders(applicationContext),
applicationContext, getWelcomePage(), this.mvcProperties.getStaticPathPattern());
}
...
@Bean
public SimpleUrlHandlerMapping faviconHandlerMapping() {
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
mapping.setUrlMap(Collections.singletonMap("**/favicon.ico", faviconRequestHandler()));
return mapping;
}
...
//等等
总结:SpringBoot程序在启动的过程中,将这些HandlerMapping注入为Bean,并为对应的实体中设置属性值。
并初始化HandlerMapping组件,在DispatchServlet#doDispatch() getHandler时,获取合适的可以使用的某一个HandlerExecutionChain
//DispatcherServlet
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}