SpringMVC处理器映射器HandlerMapping详解

目录

一、前言        

二、initHandlerMappings

三、处理器映射器架构

策略接口

请求链

模版类

四、RequestMappingHandlerMapping的初始化

HandlerMethod映射器模版类的初始化

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跨域资源共
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值