目录
四、RequestMappingHandlerMapping的初始化
AbstractHandlerMethodMapping.MappingRegistry:内部类注册中心
五、RequestMappingHandlerMapping映射器模版类的调用
RequestMappingHandlerMapping调用
一、前言
昨天在进行spring的安全升级时,遇到一个奇葩问题:原始版本为4.1.9,升级至5.3.34,由于是几年前的项目,请求路径还是带有.action的老式写法(如login.action),而有的不带(如:index),升级完后,就发现了问题,login.action能访问到,请求index时,日志爆了 no mapping for GET。
我们来看看是怎么回事。
在SpringMVC中会有很多请求,每个请求都需要一个HandlerAdapter处理,具体接收到一个请求之后使用哪个HandlerAdapter进行处理呢,他们的过程是什么。本文将对此问题进行讨论
二、initHandlerMappings
DispatcherServlet在初始化中,会调用其initHandlerMappings方法注册HandlerMapping对象并放到其缓存池中,其过程如下:先查询容器中是否有处理器映射器,如果有就注册到其缓存池中,如果没有就安装默认到规则创建处理器映射器,并注册到其缓存池中。
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
//detectAllHandlerMappings默认为true
//true标志检测所有handlerMapping,false只获取“handlerMapping”bean。
if (this.detectAllHandlerMappings) {
// 在ApplicationContext中查找所有HandlerMappings,包括祖先上下文。
Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
//排序
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
try {
//只获取“handlerMapping”bean
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.
}
}
//通过注册,确保我们至少有一个HandlerMapping
//如果找不到其他映射,则为默认HandlerMapping。
if (this.handlerMappings == null) {
//从spring-webmvc下的DispatcherServlet.properties读取默认配置
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
Spring默认HandlerMapping有BeanNameUrlHandlerMapping、RequestMappingHandlerMapping、RouterFunctionMapping
三、处理器映射器架构
策略接口
处理器映射器使用了策略模式
HandlerMapping是用来查找Handler的。在SpringMVC中会有很多请求,每个请求都需要一个Handler处理,具体接收到一个请求之后使用哪个Handler进行处理呢?这就是HandlerMapping需要做的事
HandlerMapping:负责映射用户的URL和对应的处理类Handler,HandlerMapping并没有规定这个URL与应用的处理类如何映射。所以在HandlerMapping接口中仅仅定义了根据一个URL必须返回一个由HandlerExecutionChain代表的处理链,我们可以在这个处理链中添加任意的HandlerAdapter实例来处理这个URL对应的请求(这样保证了最大的灵活性映射关系)。
请求链
模版类
处理器映射器都是实现AbstractHandlerMapping,该抽象类完成了所有的Handler以及handler里面所有的HandlerMethod的模版操作,但是怎么获取Handler,这些逻辑都是交给子类自己去实现,所以这层抽象可谓也是非常的灵活,并没有把Handler的实现方式定死。
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
implements HandlerMapping, Ordered, BeanNameAware {
//默认的Handler,这边使用的Obejct,子类实现的时候,使用HandlerMethod,HandlerExecutionChain等
@Nullable
private Object defaultHandler;
// url路径计算的辅助类、工具类
private UrlPathHelper urlPathHelper = new UrlPathHelper();
// Ant风格的Path匹配模式~ 解决如/books/{id}场景
private PathMatcher pathMatcher = new AntPathMatcher();
// 保存着拦截器们~~~
private final List<Object> interceptors = new ArrayList<>();
// 从interceptors中解析得到,直接添加给全部handler
private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<>();
// 跨域相关的配置~
private CorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
private CorsProcessor corsProcessor = new DefaultCorsProcessor();
// 最低的顺序(default: same as non-Ordered)
private int order = Ordered.LOWEST_PRECEDENCE;
@Nullable
private String beanName;
/**
* Initializes the interceptors.
* @see #extendInterceptors(java.util.List)
* @see #initInterceptors()
*/
@Override
protected void initApplicationContext() throws BeansException {
// 给子类扩展:增加拦截器,默认为空实现.RequestMappingHandlerMapping也没有重写这个方法
extendInterceptors(this.interceptors);
// 找到所有MappedInterceptor(截器是)类型的bean添加到adaptedInterceptors中
detectMappedInterceptors(this.adaptedInterceptors);
// 将interceptors中的拦截器取出放入adaptedInterceptors
// 如果是WebRequestInterceptor类型的拦截器 需要用WebRequestHandlerInterceptorAdapter进行包装适配
initInterceptors();
}
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//根据请求获取对应的处理器,子类实现
Object handler = getHandlerInternal(request);
if (handler == null) {
//如果获取不到,到默认到处理器中
handler = getDefaultHandler();
}
//如果还没有处理器,返回null
if (handler == null) {
return null;
}
// 意思是如果当前传入的handler是个String类型,那就根据其名字去容器内找这个Bean,当作一个Handler~
if (handler instanceof String) {
String handlerName = (String) handler;
//到容器中找
handler = obtainApplicationContext().getBean(handlerName);
}
//根据handler和request构造一个请求处理链~~
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
// 4.2版本提供了对CORS跨域资源共