SpringMVC 处理器映射器常用实现类源码详解
处理器的三种实现方式
处理器就是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