SpringMVC HandlerMapping(处理器映射器)常用实现类源码详解

处理器的三种实现方式

处理器就是SpringMVC中的Controller ,以下介绍三种常用的开发方式

方式一:最常用的方式@RequestMapping注解方式

这是最常用的方式,使用@Controller注解将类注入到容器中,并且类或者方法上使用@RequestMapping标记就可以开发一个注解。这里要说明的是大家常用@Controller @RestController 注解,有的人就陷入误区以为只能使用这两个注解,看如下源码:

//RequestMappingHandlerMapping类中的方法
protected boolean isHandler(Class<?> beanType) {
	return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
				AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
	}

这个方法是扫描的时候判断该类是不是处理器的方法,可以看到该类上只要是@Controller注解(包括该注解的扩展注解) 或者 @RequestMapping注解标识的类都被判断为是处理器。这里隐藏了一个前提条件,这个类是要被spring容器实例化管理的,所以单独使用@Controller注解就可以满足这两个条件,但是@RequestMapping注解标识的类是没有办法让spring实例化管理的,所以还需要配合注入实例化的注解,比如配合使用@Component @Comfiguration等。

以上是该类成为处理器的条件,成为处理器还想要暴露接口,则必须在处理器类中的方法上使用 @RequestMapping注解

这种处理器是由RequestMappingHandlerMapping 处理器映射器进行解析的。

方式二:使用BeanName方式

定义类实现Controller接口,注入到Spring容器中,BeanName必须是“/”斜杠开头。

@Component(value = "/beanName.do")
public class BeanNameUrlController implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        System.out.println("BeanNameUrl");
        return null;
    }
}

以上访问 ip:port/beanName.do即可。这种方式不能实现不同的url要访问到同一个类,并且beanName和url混合在一起,不伦不类。

这种处理器是由BeanNameUrlHandlerMapping 处理器映射器进行解析的。

方式三:使用SimpleUrl方式

开发方式同上,实现Controller接口

@Component("urlTestController")
public class UrlTestController implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        System.out.println("UrlTestController");
        return null;
    }
}

进行配置

@Configuration
public class SimpleUrlHandlerMappingConfig {

    @Bean
    public SimpleUrlHandlerMapping simpleUrlHandlerMapping(){

        SimpleUrlHandlerMapping simpleUrlHandlerMapping = new SimpleUrlHandlerMapping();
        Map<String, Object> map = new HashMap<>();
        //注册两个url访问到同一个处理器
        map.put("/simpleUrl/v1","urlTestController");
        map.put("/simpleUrl/v2","urlTestController");
        //注册第二个处理器
        map.put("/beanName","beanNameUrlController");
        simpleUrlHandlerMapping.setUrlMap(map);

        //设置该handlermapping的优先级为1,否则会被默认的覆盖,导致访问无效
        simpleUrlHandlerMapping.setOrder(1); //可以使用注册的方式覆盖默认的

        return simpleUrlHandlerMapping;
    }
}

以上访问 ip:port/simpleUrl/v1 或者 ip:port/simpleUrl/v2 都能访问到同一个处理器
这种处理器是由SimpleUrlHandlerMapping处理器映射器进行解析的。

三种处理器映射器的初始化源码分析

spring-webmvc-5.0.6.RELEASE.jar!\org\springframework\web\servlet\DispatcherServlet.properties

RequestMappingHandlerMapping初始化过程源码解析

在这里插入图片描述
以上是该类的继承体系图,特别关注的是实现的spring的接口,这些接口的实现会在spring启动的时候进行回调,从而将这个类初始化。查看InitializingBean接口会在对象初始化完成后进行回调进行设置。

RequestMappingHandlerMapping对这个接口进行了实现,方法如下:

@Override
public void afterPropertiesSet() {
	this.config = new RequestMappingInfo.BuilderConfiguration();
	this.config.setUrlPathHelper(getUrlPathHelper());
	this.config.setPathMatcher(getPathMatcher());
	this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
	this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
	this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
	this.config.setContentNegotiationManager(getContentNegotiationManager());
	//调用了父类抽象类的该方法
	super.afterPropertiesSet();
}
//父类AbstractHandlerMethodMapping的实现方法
@Override
public void afterPropertiesSet() {
	initHandlerMethods();
}

protected void initHandlerMethods() {
	if (logger.isDebugEnabled()) {
		logger.debug("...." + getApplicationContext());
	}
	
	//先获取到spring容器中注册了的所有的Bean名称
	String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
			BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :	obtainApplicationContext().getBeanNamesForType(Object.class));
	
	//循环所有名称,收集所有的处理器方法
	for (String beanName : beanNames) {
		if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
			Class<?> beanType = null;
			try {
				beanType = obtainApplicationContext().getType(beanName);
			}catch (Throwable ex) {
				if (logger.isDebugEnabled()) {
					logger.debug("...." + beanName + "'", ex);
				}
			}
			//判断类是不是处理器
			if (beanType != null && isHandler(beanType)) {
				//获取所有的方法和对应路径映射(关注点)
				detectHandlerMethods(beanName);
			}
		}
	}
	//所有映射路径收集完成后回调的方法,提供给子类覆写的方法
	handlerMethodsInitialized(getHandlerMethods());
}

//RequestMappingHandlerMapping类重写了判断是不是处理器的方法,这里可以看出处理器需要@Controller或者@RequestMapping标记,子类可以重写实现
@Override
protected boolean isHandler(Class<?> beanType) {
	return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
			AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}

//AbstractHandlerMethodMapping类收集映射路径的实现
protected void detectHandlerMethods(final Object handler) {
	//收集映射类,可能是cglib代理类
	Class<?> handlerType = (handler instanceof String ?
			obtainApplicationContext().getType((String) handler) : handler.getClass());
			
	if (handlerType != null) {
		//获取用户定义的实际类
		final Class<?> userType = ClassUtils.getUserClass(handlerType);
		//从该类中及其父类,接口中获取所有的方法进行判断和过滤,留下符合体条件的处理器方法,将@RequestMapping注解信息获取并封装
		//将以处理器中的方法为key,对应封装的RequestMappingInfo对象为value放到Map中,map中有处理器类中所有要处理的路径方法
		Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
				(MethodIntrospector.MetadataLookup<T>) method -> {
					try {
						//重点:将处理器类和方法上的@RequestMapping注解信息采集合并然后封装到RequestMappingInfo对象中
						//然后将以处理器中的方法为key,对应封装的RequestMappingInfo对象为value放到LinkedHashMap中
						return getMappingForMethod(method, userType);
					}catch (Throwable ex) { ..... }
				});
		if (logger.isDebugEnabled()) {
			logger.debug(methods.size() + " ..." + methods);
		}
		methods.forEach((method, mapping) -> {
			//获取调用的实现方法(方法可能来自本类,接口类,代理类的方法)
			Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
			//注册处理方法到MappingRegistry实例对象中
			registerHandlerMethod(handler, invocableMethod, mapping);
		});
	}
}

RequestMappingHandlerMapping中封装url映射信息的方法getMappingForMethod,这个方法可以通过子类重写进行自定义逻辑,例如统一在uri中增加版本信息作为路径条件

protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
	//获取方法上的@RequestMapping注解信息进行封装
	RequestMappingInfo info = createRequestMappingInfo(method);
	if (info != null) {
		//获取类上@RequestMapping注解信息进行封装
		RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
		if (typeInfo != null) {
			//将方法上和类上的注解信息进行合并成一个
			info = typeInfo.combine(info);
		}
	}
	return info;
}

@Nullable
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
	//获取到@RequestMapping注解
	RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
	//两个留个子类扩展的空方法,获取请求的匹配条件,需要实现RequestCondition接口进行扩展
	RequestCondition<?> condition = (element instanceof Class ?
			getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
	//创建映射信息
	return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}

//创建RequestMappingInfo,类上和方法上生成的时候都会调用这个方法
protected RequestMappingInfo createRequestMappingInfo(
		RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {
	RequestMappingInfo.Builder builder = RequestMappingInfo
			.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
			.methods(requestMapping.method())
			.params(requestMapping.params())
			.headers(requestMapping.headers())
			.consumes(requestMapping.consumes())
			.produces(requestMapping.produces())
			.mappingName(requestMapping.name());
	if (customCondition != null) {
		builder.customCondition(customCondition);
	}
	return builder.options(this.config).build();
}

//注册方法,在抽象类中,这里会判断uri的唯一性(覆写该方法可以达到uri映射多个处理方法,那就需要在实现自定义的找handler的逻辑)
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
		this.mappingRegistry.register(mapping, handler, method);
	}

最终进行方法的注册,注册到AbstractHandlerMethodMapping类的成员变量private final MappingRegistry mappingRegistry = new MappingRegistry();中,注册的封装的信息如下:

class MappingRegistry {
	//Map<RequestMappingInfo, MappingRegistration<RequestMappingInfo>> value中包含了映射路径,处理器方法等信息
	private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
	//Map<RequestMappingInfo, HandlerMethod>中,key是处理器中RequestMapping信息,value封装了处理器方法对应的所有信息
	private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
	//MultiValueMap<String, List<RequestMappingInfo>> key是类中的uri ,value是RequestMapping信息
	private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
	//这里key是根据方法名生成的一个名字,value是处理器方法信息,相同的方法名会放到同一个中,所以是list
	private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
	//存放处理器方法信息和对应跨域配置处理的映射
	private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
}

以上便是RequestMappingHandlerMapping处理器映射器初始化的一些操作,注册完成后请求过程中就可以使用对应配置处理请求。通过继承这个类,重写一些方法可以达到定制化的目的。可以重写的方法如下:

//判断类是否为handler的方法,不是很建议重写
protected boolean isHandler(Class<?> beanType)

//两个留个子类实现的空方法:RequestCondition接口扩展,根据需求
protected RequestCondition<?> getCustomTypeCondition
protected RequestCondition<?> getCustomMethodCondition(Method method)

//获取类和方法上合并后的RequestMappingInfo
protected RequestMappingInfo getMappingForMethod 
//创建RequestMappingInfo,类和方法上的创建都会调用该方法
protected RequestMappingInfo createRequestMappingInfo 
//以上两个方法获取到的方法信息可能还不是最终调用的方法,不好处理

//注册方法,在注册之前修改RequestMappingInfo信息是比较合适的,拿到的方法,处理类都是最终调用的类。推荐重写这个
protected void registerHandlerMethod(Object handler, Method method, T mapping)



//所有的处理器方法收集完后回调的方法,子类实现进行扩展,父类为空方法
//入参就是MappingRegistry类中从成员变量mappingLookup
protected void handlerMethodsInitialized(Map<T, HandlerMethod> handlerMethods)

重点说下这两个扩展接口:

protected RequestCondition<?> getCustomMethodCondition(Method method)

这个是自定义的方法级别的请求条件查询匹配方法

protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType)
这是自定义类级别的筛选条件

以上两个方法返回的都是接口RequestCondition的实现类,可以考虑通过继承AbstractRequestCondition抽象类实现,也可以使用CompositeRequestCondition构建多样化的自定义筛选条件。

当请求过来的时候,通过所有筛选条件找到RequestMappingInfo,最后调用自定义的RequestCondition接口实现类进行筛选。

public interface RequestCondition<T> {
	//上面两个方法都有实现,分别返回不同实例的时候,通过该方法合并选择返回哪个过滤条件的实例使用
	T combine(T other);
	
	//获取匹配条件,返回空匹配条件的实例对象就是匹配所有,返回null会导致匹配失败,获取handler的时候会调用这个方法
	@Nullable
	T getMatchingCondition(HttpServletRequest request);
	
	//当getMatchingCondition方法中两个都返回条件,这个方法决定用先哪一个
	int compareTo(T other, HttpServletRequest request);

}

BeanNameUrlHandlerMapping初始化过程源码解析

在这里插入图片描述
该类的类图如上图,该类的父类实现了ApplicationContextAware接口,所以这个类被spring容器管理后会调用setApplicationContext方法注入上下文。这个方法的实现在它的父类ApplicationObjectSupport中。代码如下:

public final void setApplicationContext(@Nullable ApplicationContext context) throws BeansException {
    if (context == null && !this.isContextRequired()) {
        this.applicationContext = null;
        this.messageSourceAccessor = null;
    } else if (this.applicationContext == null) {
        if (!this.requiredContextClass().isInstance(context)) {
            throw new ApplicationContextException(".....");
        }
        this.applicationContext = context;
        this.messageSourceAccessor = new MessageSourceAccessor(context);
        //关键点初始化上下文,方法在下方
        this.initApplicationContext(context);
    } else if (this.applicationContext != context) {
        throw new ApplicationContextException(".....");
    }

}
 
 //初始化上下文   
protected void initApplicationContext(ApplicationContext context) throws BeansException {
   this.initApplicationContext(); //这个方法在SimpleUrlHandlerMapping类中实现了
}

SimpleUrlHandlerMapping类中的初始化方法逻辑如下:

@Override
public void initApplicationContext() throws BeansException {
	//调用父类AbstractHandlerMapping初始化方法
	super.initApplicationContext();
	//注册本类要处理的处理器,urlMap就是创建的时候注入的url和处理器实现类的映射
	registerHandlers(this.urlMap);
}

//调用本类的注册方法,urlMap就是创建的时候注入的url和处理器实现类的映射
protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
	if (urlMap.isEmpty()) {
		logger.warn(".......");
	}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();
			}
			//调用父类AbstractHandlerMapping注册方法
			registerHandler(url, handler);
		});
	}
}

父类AbstractHandlerMapping注册方法逻辑

protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
		Object resolvedHandler = handler;

		//传入的是处理器的BeanName且不是延迟加载的Bean,从容器中获取处理器类
		if (!this.lazyInitHandlers && handler instanceof String) {
			String handlerName = (String) handler;
			ApplicationContext applicationContext = obtainApplicationContext();
			if (applicationContext.isSingleton(handlerName)) {
				resolvedHandler = applicationContext.getBean(handlerName);
			}
		}

		Object mappedHandler = this.handlerMap.get(urlPath);
		if (mappedHandler != null) {
			if (mappedHandler != resolvedHandler) {
				throw new IllegalStateException("....");
			}
		}else {
			//路径是斜杆
			if (urlPath.equals("/")) {
				if (logger.isInfoEnabled()) {
					logger.info("... " + getHandlerDescription(handler));
				}
				//设置为rootHandler
				setRootHandler(resolvedHandler);
			}else if (urlPath.equals("/*")) {
				if (logger.isInfoEnabled()) {
					logger.info("... " + getHandlerDescription(handler));
				}
				//路径是/*设置为defaultHandler
				setDefaultHandler(resolvedHandler);
			}else {
				//其余将映射关系放入Map中
				this.handlerMap.put(urlPath, resolvedHandler);
				if (logger.isInfoEnabled()) {
					logger.info(".... " + getHandlerDescription(handler));
				}
			}
		}
	}

父类AbstractHandlerMapping初始化方法逻辑

protected void initApplicationContext() throws BeansException {
	//留个子类重新的方法(暂时无重写)
	extendInterceptors(this.interceptors);
	//从上下文容器(包括父容器)中获取所有的拦截器MappedInterceptor.class的实现类封装到this.adaptedInterceptors中,
	detectMappedInterceptors(this.adaptedInterceptors);
	//初始化拦截器,经过适配封装,将所有的拦截器都封装到this.adaptedInterceptors变量中
	initInterceptors();
}

protected void initInterceptors() {
	if (!this.interceptors.isEmpty()) {
		for (int i = 0; i < this.interceptors.size(); i++) {
			Object interceptor = this.interceptors.get(i);
			//将所有拦截器适配封装,放入全局变量中
			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.getClass().getName());
	}
}

以上List adaptedInterceptors 变量是一个集合,封装所有的拦截器。

拦截器分为HandlerInterceptor与WebRequestInterceptor两种类型。

MappedInterceptor拦截器接口是HandlerInterceptor类型的子类,封装了HandlerInterceptor类拦截器和它所要拦截匹配的条件,可以直接存放在集合中

WebRequestInterceptor类型的拦截器需要进行适配封装,WebRequestHandlerInterceptorAdapter实现了HandlerInterceptor接口,将WebRequestInterceptor类型的拦截器使用适配器模式进行封装后存入集合中存储。

SimpleUrlHandlerMapping初始化过程源码解析

在这里插入图片描述
查看类的继承体系图,对比BeanNameUrlHandlerMapping类的图可以看出。这两个类继承体系是一样的,只是SimpleUrlHandlerMapping类多了一层抽象AbstractDetectingUrlHandlerMapping类。所以它们的初始化流程开始都是一样的,要查看AbstractDetectingUrlHandlerMapping的重写方法即可。

AbstractDetectingUrlHandlerMapping重写了initApplicationContext方法如下:

public void initApplicationContext() throws ApplicationContextException {	
	//先是调用父类(AbstractHandlerMapping)的初始化方法
	//这里跟上一个类BeanNameUrlHandlerMapping是一样的初始化流程
	super.initApplicationContext();
	//收集所有的url进行注册
	detectHandlers();
}

```java
protected void detectHandlers() throws BeansException {
	ApplicationContext applicationContext = obtainApplicationContext();
	if (logger.isDebugEnabled()) {
		logger.debug("..... " + applicationContext);
	}
	
	//拿到上下文容器中所有注册了的BeanName
	String[] beanNames = (this.detectHandlersInAncestorContexts ?
			BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) :
			applicationContext.getBeanNamesForType(Object.class));
	
	//循环注册所有需要注册的url和Bean名称的映射
	for (String beanName : beanNames) {
		//子类实现的方法,将bean名称以/开头的收集起来(包括别名)作为url
		String[] urls = determineUrlsForHandler(beanName);
		if (!ObjectUtils.isEmpty(urls)) {
			//找到需要注册的Bean名称和对应的所有url后进行注册,与类BeanNameUrlHandlerMapping是一样的
			registerHandler(urls, beanName);
		}
		else {
			if (logger.isDebugEnabled()) {
				logger.debug("....");
			}
		}
	}
}

BeanNameUrlHandlerMapping类实现determineUrlsForHandler方法

@Override
protected String[] determineUrlsForHandler(String beanName) {
	List<String> urls = new ArrayList<>();
	if (beanName.startsWith("/")) {
		urls.add(beanName);
	}
	String[] aliases = obtainApplicationContext().getAliases(beanName);
	for (String alias : aliases) {
		if (alias.startsWith("/")) {
			urls.add(alias);
		}
	}
	return StringUtils.toStringArray(urls);
}

总结

以上是三个处理器映射器的初始化过程跟踪,如果想要了解springMVC的处理流程,了解这三个处理器映射器还是非常有必要的。
上面说的是初始化流程,那这三个类系统默认创建的是从哪里开始实例化的呢?
查看一下两个类你就会找到答案了。
WebMvcAutoConfiguration
WebMvcConfigurationSupport

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值