從本文開始,來分析下springmvc幾個(gè)重要組件是如何工作的。
首選,來看下HandlerMapping
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
這個(gè)接口主要有這個(gè)方法,通過request獲取一個(gè)HandlerExecutionChain對(duì)象,這個(gè)對(duì)象就是包含了兩個(gè)成員變量
//handler就是應(yīng)用程序中定義的controller
private final Object handler;
//攔截器,在controller方法的執(zhí)行前后加入自己的邏輯,類似于切面
private final List<HandlerInterceptor> interceptorList = new ArrayList<>();
簡(jiǎn)單的說,HandlerMapping就是通過http請(qǐng)求查詢到對(duì)應(yīng)controller和interceptor。
springmvc默認(rèn)提供的實(shí)現(xiàn)類有三個(gè),分別為RequestMappingHandlerMapping、BeanNameUrlHandlerMapping、RouterFunctionMapping。
日常開發(fā)中用的最多的便是RequestMappingHandlerMapping,因此本文主要來看下這個(gè)這個(gè)bean對(duì)象的初始化流程
初始化流程便是定義在WebMvcConfigurationSupport類中
@Bean
@SuppressWarnings("deprecation")
public RequestMappingHandlerMapping requestMappingHandlerMapping(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
mapping.setOrder(0);
mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
mapping.setContentNegotiationManager(contentNegotiationManager);
mapping.setCorsConfigurations(getCorsConfigurations());
PathMatchConfigurer pathConfig = getPathMatchConfigurer();
if (pathConfig.getPatternParser() != null) {
mapping.setPatternParser(pathConfig.getPatternParser());
}
else {
mapping.setUrlPathHelper(pathConfig.getUrlPathHelperOrDefault());
mapping.setPathMatcher(pathConfig.getPathMatcherOrDefault());
Boolean useSuffixPatternMatch = pathConfig.isUseSuffixPatternMatch();
if (useSuffixPatternMatch != null) {
mapping.setUseSuffixPatternMatch(useSuffixPatternMatch);
}
Boolean useRegisteredSuffixPatternMatch = pathConfig.isUseRegisteredSuffixPatternMatch();
if (useRegisteredSuffixPatternMatch != null) {
mapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch);
}
}
Boolean useTrailingSlashMatch = pathConfig.isUseTrailingSlashMatch();
if (useTrailingSlashMatch != null) {
mapping.setUseTrailingSlashMatch(useTrailingSlashMatch);
}
if (pathConfig.getPathPrefixes() != null) {
mapping.setPathPrefixes(pathConfig.getPathPrefixes());
}
return mapping;
}
首先就是設(shè)置order,值越小,越先執(zhí)行。程序中可能存在多個(gè)HandlerMapping實(shí)現(xiàn)類,因此執(zhí)行的先后順序是可以指定的。
然后就是可以添加攔截器interceptor
protected final Object[] getInterceptors(
FormattingConversionService mvcConversionService,
ResourceUrlProvider mvcResourceUrlProvider) {
if (this.interceptors == null) {
InterceptorRegistry registry = new InterceptorRegistry();
addInterceptors(registry);
registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService));
registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider));
this.interceptors = registry.getInterceptors();
}
return this.interceptors.toArray();
}
springmvc默認(rèn)會(huì)添加兩個(gè)攔截器ConversionServiceExposingInterceptor和ResourceUrlProviderExposingInterceptor,這里有一個(gè)很重要的方法addInterceptors,這方法就是給子類繼承重寫,讓應(yīng)用程序可以手動(dòng)添加interceptor
每個(gè)攔截器是一個(gè)InterceptorRegistration對(duì)象,包含了三個(gè)成員變量
//攔截器對(duì)象
private final HandlerInterceptor interceptor;
//符合攔截器執(zhí)行的路徑模式
private final List<String> includePatterns = new ArrayList<>();
//符合攔截器執(zhí)行的路徑模式
private final List<String> excludePatterns = new ArrayList<>();
//路徑匹配器
@Nullable
private PathMatcher pathMatcher;
//攔截器的執(zhí)行順序
private int order = 0;
HandlerMapping除了添加攔截器外,還有一個(gè)比較重要的方法,是添加跨域信息相關(guān)的,同樣有個(gè)方法addCorsMappings可以讓子類重寫,由應(yīng)用程序添加跨域相關(guān)的配置。
HandlerMapping其他屬性由于應(yīng)用程序一般不需要去修改,因此這里不再講述了,當(dāng)生成RequestMappingHandlerMapping對(duì)象后,由于這個(gè)類還實(shí)現(xiàn)了InitializingBean接口,因此還會(huì)執(zhí)行初始化方法afterPropertiesSet
public void afterPropertiesSet() {
this.config = new RequestMappingInfo.BuilderConfiguration();
this.config.setUrlPathHelper(getUrlPathHelper());
this.config.setPathMatcher(getPathMatcher());
this.config.setSuffixPatternMatch(useSuffixPatternMatch());
this.config.setTrailingSlashMatch(useTrailingSlashMatch());
this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());
this.config.setContentNegotiationManager(getContentNegotiationManager());
super.afterPropertiesSet();
}
可以看到這個(gè)方法主要是將HandlerMapping的相關(guān)屬性值賦值給內(nèi)部成員RequestMappingInfo.BuilderConfiguration的屬性,然后再調(diào)用initHandlerMethods方法
protected void initHandlerMethods() {
for (String beanName : getCandidateBeanNames()) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
processCandidateBean(beanName);
}
}
}
這個(gè)方法中,循環(huán)處理容器的beanName列表,然后取出beanName對(duì)應(yīng)的Class對(duì)象,判斷該class對(duì)象是否為Handler,判斷標(biāo)準(zhǔn)就是看類是否有注解了Controller或者RequestMapping,若是Handler,則執(zhí)行detectHandlerMethods
protected void detectHandlerMethods(Object handler) {
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
Class<?> userType = ClassUtils.getUserClass(handlerType);
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
//根據(jù)Method對(duì)象,獲取對(duì)應(yīng)的RequestMappingInfo對(duì)象
return getMappingForMethod(method, userType);
}
catch (Throwable ex) {
throw new IllegalStateException("Invalid mapping on handler class [" +
userType.getName() + "]: " + method, ex);
}
});
//....中間日志省略
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
這個(gè)方法主要分為兩個(gè)步驟
- MethodIntrospector.selectMethods 獲取handler(controller)下Method對(duì)象及其對(duì)應(yīng)的RequestMappingInfo對(duì)象,其中RequestMappingInfo對(duì)象就是對(duì)方法注解@RequestMapping相關(guān)屬性值的封裝(也會(huì)結(jié)合注解在類上面的@RequestMapping)
- 將Method對(duì)象,RequestMappingInfo對(duì)象維護(hù)到HandlerMapping中,當(dāng)然還有包含url相關(guān)的,這樣后續(xù)請(qǐng)求到來,就可根據(jù)url映射出對(duì)應(yīng)的handler。
上述步驟1涉及到的細(xì)節(jié)很多,這里不展開了,主要來看下步驟2,registerHandlerMethod的處理
public void register(T mapping, Object handler, Method method) {
this.readWriteLock.writeLock().lock();
try {
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
validateMethodMapping(handlerMethod, mapping);
this.mappingLookup.put(mapping, handlerMethod);
List<String> directUrls = getDirectUrls(mapping);
for (String url : directUrls) {
this.urlLookup.add(url, mapping);
}
String name = null;
if (getNamingStrategy() != null) {
name = getNamingStrategy().getName(handlerMethod, mapping);
addMappingName(name, handlerMethod);
}
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
this.corsLookup.put(handlerMethod, corsConfig);
}
this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
}
finally {
this.readWriteLock.writeLock().unlock();
}
}
這方法主要是對(duì)HandlerMapping內(nèi)部一些map的維護(hù)
//RequestMappingInfo,MappingRegistration
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
//RequestMappingInfo, HandlerMethod
private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
//url, RequestMappingInfo列表
private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
//name, HandlerMethod
private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
//cors
private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
通過這些map,程序可以快速的根據(jù)請(qǐng)求url定位到具體的handler,再執(zhí)行對(duì)應(yīng)的方法。這個(gè)請(qǐng)求的流程后續(xù)再講述。
總結(jié),RequestMapping實(shí)例化主要是對(duì)內(nèi)部重要的屬性進(jìn)行賦值,方便應(yīng)用程序可根據(jù)http請(qǐng)求的相關(guān)信息,比如url,header頭等等,找到對(duì)應(yīng)的Controller,并執(zhí)行相應(yīng)的方法。