1、HandlerMapping接口继承结构体系
B0373966-7A31-4C09-912F-724CFA9243D6.png2、HandlerMapping接口功能
HandlerMapping接口中只定义了一个方法
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
该方法很简单,就是根据请求的request,获取HandlerExecutionChain对象。
3、HandlerMapping家族成员
从HandlerMapping接口继承结构体系图中,我们可以发现HandlerMapping家族可以分为两支,一支继承AbstractUrlHandlerMapping,一支继承AbstractHandlerMethodMapping,这两支都继承自AbstractHandlerMapping。其中AbstractUrlHandlerMapping在目前大部分的项目使用中,已经很少使用到,本篇文章会稍微分析下SimpleUrlHandlerMapping子类。重点分析AbstractHandlerMethodMapping,他就是我们经常使用的@RequestMapping
注解会使用到的方式。下面我们先分析这个父类,再分别分析这两个分支。
4、AbstractHandlerMapping
4.1、AbstractHandlerMapping概述
AbstractHandlerMapping是HandlerMapping的抽象实现,采用模板模式设计了HandlerMapping的整体架构。其定义了getHandlerInternal方法,根据request来获取Handler,由子类来具体实现该方法。然后再根据request来获取相应的interceptors,整合从子类获取的Handler,组成HandlerExecutionChain对象返回。
4.2、AbstractHandlerMapping初始化
AbstractHandlerMapping继承了WebApplicationObjectSupport,初始化时会自动调用模板方法initApplicationContext,具体如下:
@Override
protected void initApplicationContext() throws BeansException {
// 模板方法,暂无子类实现
extendInterceptors(this.interceptors);
// 从容器中获取实现了MappedInterceptor接口的对象,添加到adaptedInterceptors列表中
detectMappedInterceptors(this.adaptedInterceptors);
// 将interceptors(由子类添加)中的对象封装后添加到adaptedInterceptors列表中
initInterceptors();
}
4.3 AbstractHandlerMapping的使用
AbstractHandlerMapping的模板模式就是从如下方法开始的
@Override
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 模板模式方法,具体由子类实现
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
// 根据request从adaptedInterceptors中选择匹配的interceptors,与handler一起封装成HandlerExecutionChain对象
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
return executionChain;
}
下面具体分析两个分支AbstractUrlHandlerMapping和AbstractHandlerMethodMapping
5、AbstractUrlHandlerMapping
5.1 AbstractUrlHandlerMapping概述
AbstractUrlHandlerMapping实现了父类的模板方法getHandlerInternal
@Override
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
Object handler = lookupHandler(lookupPath, request);
if (handler == null) {
Object rawHandler = null;
if ("/".equals(lookupPath)) {
rawHandler = getRootHandler();
}
if (rawHandler == null) {
rawHandler = getDefaultHandler();
}
if (rawHandler != null) {
// Bean name or resolved handler?
if (rawHandler instanceof String) {
String handlerName = (String) rawHandler;
rawHandler = getApplicationContext().getBean(handlerName);
}
validateHandler(rawHandler, request);
handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
}
}
if (handler != null && logger.isDebugEnabled()) {
logger.debug("Mapping [" + lookupPath + "] to " + handler);
}
else if (handler == null && logger.isTraceEnabled()) {
logger.trace("No handler mapping found for [" + lookupPath + "]");
}
return handler;
}
其中最重要的就是lookupHandler方法,具体代码就不贴了,大体思路是说一下。AbstractUrlHandlerMapping类中定义了一个Map<String, Object>
类型的变量handlerMap
,用来存储url与handler的对应关系,lookupHandler
方法就是根据url在该变量中找到相应的handler。其中url是可以根据正则表达式来查找的,所以lookupHandler
方法中还会根据url可能会查找到多个,然后根据正则的匹配关系找到最符合的handler对象返回。那handlerMap
变量中的数据又是从哪来的呢?带着这个问题我们继续分析下去。我们看到AbstractUrlHandlerMapping类中提供了两个注册handler方法
protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException
这两个方法就是提供给子类注册handler了的,子类通过调用这两个注册方法就可以将url与handler的对应关系存储到handlerMap
变量中了。那问题又来了,子类又是如何获取url与handler的对应关系的呢?针对AbstractUrlHandlerMapping的子类,我们只分析下SimpleUrlHandlerMapping类。
5.2 SimpleUrlHandlerMapping
5.5.1 SimpleUrlHandlerMapping的使用
上面也提过,目前大部分项目都是使用@RequestMapping
的方式来定义Handler,SimpleUrlHandlerMapping类很少会使用到,所以这里先介绍下该类的使用,方便后续对其使用原理的讲解。
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="simpleUrlController">simleUrl</prop>
</props>
</property>
</bean>
<bean id="simleUrl" class="com.zhao.web.SimpleUrlController" />
SimpleUrlHandlerMapping的使用非常简单,如上在spring mvc的配置文件中指定好url与bean的对应关系即刻,其中该bean类需要实现Controller接口。
5.5.2 SimpleUrlHandlerMapping原理
如上定义的url与bean的对应关系,最终会被解析设置到SimpleUrlHandlerMapping类中的urlmap变量中。而SimpleUrlHandlerMapping类重写了父类AbstractHandlerMapping的initApplicationContext方法
@Override
public void initApplicationContext() throws BeansException {
// 调用父类AbstractHandlerMapping的方法,主要功能是实现interceptor的初始化
super.initApplicationContext();
// 将本类中的urlmap注册到父类AbstractUrlHandlerMapping的handlerMap变量中
registerHandlers(this.urlMap);
}
6、AbstractHandlerMethodMapping
6.1 AbstractHandlerMethodMapping概述
与AbstractUrlHandlerMapping一样,AbstractHandlerMethodMapping也是实现了父类的模板方法getHandlerInternal
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
if (logger.isDebugEnabled()) {
logger.debug("Looking up handler method for path " + lookupPath);
}
this.mappingRegistry.acquireReadLock();
try {
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
if (logger.isDebugEnabled()) {
if (handlerMethod != null) {
logger.debug("Returning handler method [" + handlerMethod + "]");
}
else {
logger.debug("Did not find handler method for [" + lookupPath + "]");
}
}
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
代码很简单,主要的实现逻辑就是lookupHandlerMethod方法
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<Match>();
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
// No choice but to go through all mappings...
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}
if (!matches.isEmpty()) {
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
Collections.sort(matches, comparator);
if (logger.isTraceEnabled()) {
logger.trace("Found " + matches.size() + " matching mapping(s) for [" +
lookupPath + "] : " + matches);
}
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
if (CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
}
}
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
}
else {
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
其中T是来自AbstractHandlerMethodMapping类的定义,目前SpringMVC中只有RequestMappingInfo的实现。RequestMappingInfo类主要是对多种类型的请求匹配对象的封装,包括请求url、cookie、header等。
在分析lookupHandlerMethod方法的整体思路之前,我们还得知晓AbstractHandlerMethodMapping的内部类MappingRegistry。MappingRegistry类中定义了两个比较重要的变量,Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<T, HandlerMethod>()
和MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<String, T>()
。其中mappingLookup变量保存了RequestMappingInfo与HandlerMethod的一一对应关系,而urlLookup变量则保存了url与RequestMappingInfo的对应关系,需要注意的是MultiValueMap类型的变量是可以一个key对应多个value的,也就是说urlLookup变量中,一个url可能对应多个RequestMappingInfo。
好,现在继续看lookupHandlerMethod方法,首选根据lookupPath(可以理解为url)获取到RequestMappingInfo,就是从urlLookup获取,可能返回多个对象,最后找到一个最符合的RequestMappingInfo对象,然后根据该对象再去获取到HandlerMethod对象返回。大体思路如此,具体细节不做更多分析。
与分析AbstractUrlHandlerMapping类时一致,这里的mappingLookup和urlLookup中的数据又是何时初始化的呢?AbstractHandlerMethodMapping实现了InitializingBean接口,所以spring容器会自动调用其afterPropertiesSet方法,该方法又交给initHandlerMethods方法完成具体的初始化。
protected void initHandlerMethods() {
if (logger.isDebugEnabled()) {
logger.debug("Looking for request mappings in application context: " + getApplicationContext());
}
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
for (String beanName : beanNames) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
Class<?> beanType = null;
try {
beanType = getApplicationContext().getType(beanName);
}
catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
if (logger.isDebugEnabled()) {
logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
}
}
if (beanType != null && isHandler(beanType)) {
detectHandlerMethods(beanName);
}
}
}
// 模板方法,暂无子类实现
handlerMethodsInitialized(getHandlerMethods());
}
逻辑不是很复杂,首先从spring容器中获取所有的bean,然后进行过滤与处理。主要关注其中的两个方法,isHandler(beanType)
与detectHandlerMethods(beanName)
。其中isHandler(beanType)
方法由子类RequestMappingHandlerMapping实现,用于对bean进行过滤,判断是否包含Controller或者RequestMapping注解。
@Override
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
而detectHandlerMethods(beanName)
方法,则根据筛选出的bean,进行进一步的处理,也就是对上述的mappingLookup和urlLookup两个变量的数据填充。
protected void detectHandlerMethods(final Object handler) {
Class<?> handlerType = (handler instanceof String ?
getApplicationContext().getType((String) handler) : handler.getClass());
// CGLib动态代理的特殊处理
final Class<?> userType = ClassUtils.getUserClass(handlerType);
// methods包含了bean中所有符合条件的method与相关的RequestMappingInfo键值对
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
new MethodIntrospector.MetadataLookup<T>() {
@Override
public T inspect(Method method) {
try {
// 如果method有@RequestMapping注解,则返回由注解得到的封装好的RequestMappingInfo对象,否则返回null
return getMappingForMethod(method, userType);
}
catch (Throwable ex) {
throw new IllegalStateException("Invalid mapping on handler class [" +
userType.getName() + "]: " + method, ex);
}
}
});
if (logger.isDebugEnabled()) {
logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
}
for (Map.Entry<Method, T> entry : methods.entrySet()) {
Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
T mapping = entry.getValue();
// 向mappingLookup和urlLookup两个变量中填充数据
registerHandlerMethod(handler, invocableMethod, mapping);
}
}
网友评论