java?spring?mvc處理器映射器介紹
前言:
- 本文源碼基于
spring-framework-5.3.10。 mvc是spring源碼中的一個(gè)子模塊!
一、RequestMappingHandlerMapping解析映射簡(jiǎn)單介紹
- @RequestMapping通過
RequestMappingHandlerMapping進(jìn)行解析! - HandlerMapping是一個(gè)根據(jù)URL映射到Handler的方法。
RequestMappingHandlerMapping是HandlerMapping的一個(gè)子類!- RequestMappingHandlerMapping他的父類有InitializingBean,所有在spring啟動(dòng)實(shí)例化的時(shí)候會(huì)調(diào)用afterPropertiesSet()方法。解析邏輯就在這里。
RequestMappingHandlerMapping有倆個(gè)過程:解析、映射
二、@RequestMapping解析源碼流程
- 容器加載,調(diào)用
RequestMappingHandlerMapping的afterPropertiesSet()。 - 調(diào)用父類的
afterPropertiesSet()方法。 - 調(diào)用initHandlerMethods()方法。
- 循環(huán)每一個(gè)Bean,看方法上有@RequestMapping或者@Controller的Bean。
- 解析
HandlerMethods,進(jìn)行封裝RequestMappingInfo。 - 將封裝好的
RequestMappingInfo存起來:key為路徑,值為mapping(RequestMappingInfo)
三、@RequestMapping映射源碼流程
- 請(qǐng)求進(jìn)來,調(diào)用
getHandler方法。 - 獲取當(dāng)前請(qǐng)求對(duì)應(yīng)的
HandlerMethod。 - 通過
UrlPathHelper對(duì)象,用于來解析從們的request中解析出請(qǐng)求映射路徑。 - 更具路徑去
pathLookup中找。 - 上面沒找到,從所有的里面找有通配符的。
- 找到多個(gè)進(jìn)行排序,優(yōu)先級(jí):? > * > {} >** 。
- 不為空拿到第一個(gè)返回。
- 如果為空獲取默認(rèn)的。默認(rèn)還是空的,直接返回null。
- 封裝攔截器,返回。
四、@RequestMapping解析源碼
/**
* 解析的開始位置。
* 由于實(shí)現(xiàn)了InitializingBean,初始化Bean的時(shí)候調(diào)用這個(gè)方法。
* 源碼位置:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.afterPropertiesSet()
*/
public void afterPropertiesSet() {
this.config = new RequestMappingInfo.BuilderConfiguration();
this.config.setTrailingSlashMatch(useTrailingSlashMatch()); // 尾部斜杠
this.config.setContentNegotiationManager(getContentNegotiationManager());
if (getPatternParser() != null) {
this.config.setPatternParser(getPatternParser());
Assert.isTrue(!this.useSuffixPatternMatch && !this.useRegisteredSuffixPatternMatch,
"Suffix pattern matching not supported with PathPatternParser.");
}
else {
this.config.setSuffixPatternMatch(useSuffixPatternMatch());
this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());
this.config.setPathMatcher(getPathMatcher());
}
// 調(diào)用父類的afterPropertiesSet方法
super.afterPropertiesSet();
}
/**
* 父類的afterPropertiesSet方法。
* 源碼位置:org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.afterPropertiesSet()
*/
public void afterPropertiesSet() {
initHandlerMethods();
}
/**
* 解析@RequestMapping方法
* 源碼位置:org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.initHandlerMethods()
*/
protected void initHandlerMethods() {
// 獲得所有候選beanName—— 當(dāng)前容器所有的beanName
for (String beanName : getCandidateBeanNames()) {
// BeanName不是scopedTarget.開頭的
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
// *處理候選bean——即解析@RequestMapping和映射路徑
processCandidateBean(beanName);
}
}
// 解析完所有@RequestMapping的時(shí)候調(diào)用
handlerMethodsInitialized(getHandlerMethods());
}
/**
* 處理候選bean——即解析@RequestMapping和映射路徑
* 源碼位置:org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.processCandidateBean(String)
*/
protected void processCandidateBean(String beanName) {
Class<?> beanType = null;
try {
// 得到當(dāng)前BeanName得到這個(gè)Bean的類型
beanType = obtainApplicationContext().getType(beanName);
}
catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
if (logger.isTraceEnabled()) {
logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
}
}
// 這一步判斷是關(guān)鍵:是否有Controller 或 RequestMapping注解
if (beanType != null && isHandler(beanType)) {
// 解析HandlerMethods
detectHandlerMethods(beanName);
}
}
/**
* 解析HandlerMethods
* 源碼位置:org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.detectHandlerMethods(Object)
*/
protected void detectHandlerMethods(Object handler) {
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
Class<?> userType = ClassUtils.getUserClass(handlerType);
// 循環(huán)所有方法
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
// 根據(jù)Method得到Mapping映射
return getMappingForMethod(method, userType);
}
catch (Throwable ex) {
throw new IllegalStateException("Invalid mapping on handler class [" +
userType.getName() + "]: " + method, ex);
}
});
if (logger.isTraceEnabled()) {
logger.trace(formatMappings(userType, methods));
}
else if (mappingsLogger.isDebugEnabled()) {
mappingsLogger.debug(formatMappings(userType, methods));
}
// 遍歷每一個(gè)方法,這里是所有Bean的所有方法
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
// pathLookup放入:key為路徑,值為mapping(RequestMappingInfo)
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
/**
* 根據(jù)Method得到Mapping映射
* 源碼位置:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.getMappingForMethod(Method, Class<?>)
*/
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
// 如果方法上面有@RequestMapping:解析出RequestMappingInfo
// RequestMappingInfo 是用來在請(qǐng)求的時(shí)候做匹對(duì)的
RequestMappingInfo info = createRequestMappingInfo(method);
if (info != null) {
// 如果方法上面有@RequestMapping,看看類上面是不是有@RequestMapping
RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
// 類上面也有@RequestMapping 那就合并
// 比如 類:/user 方法:/info 合并為 /user/info
if (typeInfo != null) {
info = typeInfo.combine(info);
}
// 合并前綴 5.1新增 默認(rèn)null
// 可通過 WebMvcConfigurer#configurePathMatch 進(jìn)行定制
String prefix = getPathPrefix(handlerType);
if (prefix != null) {
info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
}
}
return info;
}
/**
* 創(chuàng)建請(qǐng)求映射信息的外部邏輯
* 源碼位置:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.createRequestMappingInfo(AnnotatedElement)
*/
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
// 獲取RequestMapping注解信息
RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
// 獲取請(qǐng)求調(diào)解:[可擴(kuò)展], 如果有:該條件會(huì)在請(qǐng)求時(shí)匹對(duì)
RequestCondition<?> condition = (element instanceof Class ?
getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
// 如果有RequestMapping注解,封裝成RequestMappingInfo
return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}
/**
* 創(chuàng)建請(qǐng)求映射信息的內(nèi)部邏輯
* 源碼位置:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.createRequestMappingInfo(RequestMapping, RequestCondition<?>)
*/
protected RequestMappingInfo createRequestMappingInfo(
RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {
// 將@RequestMapping注解屬性的值構(gòu)建成一個(gè) RequestMappingInfo
RequestMappingInfo.Builder builder = RequestMappingInfo
//構(gòu)建路徑
.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
//構(gòu)建方法(get還是post等)
.methods(requestMapping.method())
//參數(shù) 對(duì)應(yīng)http request parameter
.params(requestMapping.params())
//頭部
.headers(requestMapping.headers())
//request的提交內(nèi)容類型content type,如application/json, text/html
.consumes(requestMapping.consumes())
//指定返回的內(nèi)容類型的content type,僅當(dāng)request請(qǐng)求頭中的(Accept)類型中包含該指定類型才返回
.produces(requestMapping.produces())
.mappingName(requestMapping.name());
if (customCondition != null) {
builder.customCondition(customCondition);
}
// 構(gòu)造RequestMappingInfo:將上面的屬性構(gòu)建成一個(gè)個(gè)的RequestCondition對(duì)象方便在請(qǐng)求的時(shí)候組合匹對(duì)
return builder.options(this.config).build();
}
/**
* 得到所有的方法
* 源碼位置:org.springframework.core.MethodIntrospector.selectMethods(Class<?>, MetadataLookup<T>)
*/
public static <T> Map<Method, T> selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup) {
final Map<Method, T> methodMap = new LinkedHashMap<>();
Set<Class<?>> handlerTypes = new LinkedHashSet<>();
Class<?> specificHandlerType = null;
//獲取原始的class對(duì)象
if (!Proxy.isProxyClass(targetType)) {
specificHandlerType = ClassUtils.getUserClass(targetType);
handlerTypes.add(specificHandlerType);
}
//獲取class的接口
handlerTypes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetType));
//循環(huán)我們的class集合
for (Class<?> currentHandlerType : handlerTypes) {
final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);
ReflectionUtils.doWithMethods(currentHandlerType, method -> {
//獲取具體的方法對(duì)象
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
/**回調(diào) 即解析@RequestMapping 返回RequestMappingInfo
* @see org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#getMappingForMethod(java.lang.reflect.Method, java.lang.Class)*/
T result = metadataLookup.inspect(specificMethod);
if (result != null) {
// 看看有沒有橋接方法:泛型實(shí)現(xiàn)類jvm會(huì)自動(dòng)生成橋接類
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {
//把方法對(duì)象作為key,RequestMappingInfo對(duì)象作為value保存到map中
methodMap.put(specificMethod, result);
}
}
}, ReflectionUtils.USER_DECLARED_METHODS);
}
return methodMap;
}
五、@RequestMapping映射源碼
/**
* 獲取@RequestMapping映射
* 源碼位置:org.springframework.web.servlet.handler.AbstractHandlerMapping.getHandler(HttpServletRequest)
*/
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 獲取當(dāng)前請(qǐng)求對(duì)應(yīng)的HandlerMethod
Object handler = getHandlerInternal(request);
// 獲取默認(rèn)的handler
if (handler == null) {
handler = getDefaultHandler();
}
// 還是沒有handler的時(shí)候返回null,404了
if (handler == null) {
return null;
}
// Bean name or resolved handler?
// String類型?獲取Bean
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
// Ensure presence of cached lookupPath for interceptors and others
if (!ServletRequestPathUtils.hasCachedPath(request)) {
initLookupPath(request);
}
// 獲取攔截器相關(guān)的調(diào)用鏈
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
}
else if (logger.isDebugEnabled() && !DispatcherType.ASYNC.equals(request.getDispatcherType())) {
logger.debug("Mapped to " + executionChain.getHandler());
}
if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
CorsConfiguration config = getCorsConfiguration(handler, request);
if (getCorsConfigurationSource() != null) {
CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request);
config = (globalConfig != null ? globalConfig.combine(config) : config);
}
if (config != null) {
config.validateAllowCredentials();
}
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
/**
* 獲取當(dāng)前請(qǐng)求對(duì)應(yīng)的HandlerMethod
* 源碼位置:org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping.getHandlerInternal(HttpServletRequest)
*/
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
request.removeAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
try {
// 直接調(diào)用父類的getHandlerInternal方法
return super.getHandlerInternal(request);
}
finally {
ProducesRequestCondition.clearMediaTypesAttribute(request);
}
}
/**
* 獲取當(dāng)前請(qǐng)求對(duì)應(yīng)的HandlerMethod---父類的
* 源碼位置:org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(HttpServletRequest)
*/
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
// 通過UrlPathHelper對(duì)象,用于來解析從們的request中解析出請(qǐng)求映射路徑
String lookupPath = initLookupPath(request);
this.mappingRegistry.acquireReadLock();
try {
// 通過lookupPath解析最終的handler——HandlerMethod對(duì)象
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
/**
* 通過lookupPath解析最終的handler
* 源碼位置:org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.lookupHandlerMethod(String, HttpServletRequest)
*/
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
// 根據(jù)uri從mappingRegistry.pathLookup獲取 RequestMappingInfo
// pathLookup<path,RequestMappingInfo>會(huì)在初始化階段解析好
List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
if (directPathMatches != null) {
// 如果根據(jù)path能直接匹配的RequestMappingInfo 則用該mapping進(jìn)行匹配其他條件(method、header等)
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
// 如果無path匹配,用所有的RequestMappingInfo 通過AntPathMatcher匹配
addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
}
if (!matches.isEmpty()) {
// 選擇第一個(gè)為最匹配的
Match bestMatch = matches.get(0);
/**
* 如果匹配到多個(gè)
@RequestMapping(value="/mappin?")
@RequestMapping(value="/mappin*")
@RequestMapping(value="/{xxxx}")
@RequestMapping(value="/**")
*/
if (matches.size() > 1) {
//創(chuàng)建MatchComparator的匹配器對(duì)象
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
/** 根據(jù)精準(zhǔn)度排序 大概是這樣的: ? > * > {} >** 具體可以去看:
* @see org.springframework.util.AntPathMatcher.AntPatternComparator#compare(java.lang.String, java.lang.String)*/
matches.sort(comparator);
// 排完序后拿到優(yōu)先級(jí)最高的
bestMatch = matches.get(0);
if (logger.isTraceEnabled()) {
logger.trace(matches.size() + " matching mappings: " + matches);
}
// 是否配置CORS并且匹配
if (CorsUtils.isPreFlightRequest(request)) {
for (Match match : matches) {
if (match.hasCorsConfig()) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
}
}
else {
//獲取第二最匹配的
Match secondBestMatch = matches.get(1);
//若第一個(gè)和第二個(gè)是一樣的 拋出異常
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.getHandlerMethod().getMethod();
Method m2 = secondBestMatch.getHandlerMethod().getMethod();
String uri = request.getRequestURI();
throw new IllegalStateException(
"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
}
}
}
//把最匹配的設(shè)置到request中
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());
handleMatch(bestMatch.mapping, lookupPath, request);
//返回最匹配的
return bestMatch.getHandlerMethod();
}
else { // return null
return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
}
}到此這篇關(guān)于java spring mvc處理器映射器介紹的文章就介紹到這了,更多相關(guān)spring mvc處理器映射器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring Boot 整合 ShedLock 處理定時(shí)任務(wù)重復(fù)執(zhí)行的問題小結(jié)
ShedLock是解決分布式系統(tǒng)中定時(shí)任務(wù)重復(fù)執(zhí)行問題的Java庫,通過在數(shù)據(jù)庫中加鎖,確保只有一個(gè)節(jié)點(diǎn)在指定時(shí)間執(zhí)行任務(wù),它與SpringScheduler、Quartz等框架結(jié)合使用,本文介紹Spring Boot 整合 ShedLock 處理定時(shí)任務(wù)重復(fù)執(zhí)行的問題,感興趣的朋友一起看看吧2025-02-02
idea插件篇之java內(nèi)存分析工具(JProfiler)的使用
這篇文章主要介紹了idea插件篇之java內(nèi)存分析工具(JProfiler),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
SpringBoot 項(xiàng)目中的圖片處理策略之本地存儲(chǔ)與路徑映射
在SpringBoot項(xiàng)目中,靜態(tài)資源存放在static目錄下,使得前端可以通過URL來訪問這些資源,我們就需要將文件系統(tǒng)的文件路徑與URL建立一個(gè)映射關(guān)系,把文件系統(tǒng)中的文件當(dāng)成我們的靜態(tài)資源即可,本文給大家介紹SpringBoot本地存儲(chǔ)與路徑映射的相關(guān)知識(shí),感興趣的朋友一起看看吧2023-12-12
SpringCloud feign服務(wù)熔斷下的異常處理操作
這篇文章主要介紹了SpringCloud feign服務(wù)熔斷下的異常處理操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06
SpringBoot?Schedule調(diào)度任務(wù)的動(dòng)態(tài)管理
Scheduled定時(shí)任務(wù)是Spring?boot自身提供的功能,所以不需要引入Maven依賴包,下面這篇文章主要給大家介紹了關(guān)于SpringBoot通過@Scheduled實(shí)現(xiàn)定時(shí)任務(wù)以及問題解決的相關(guān)資料,需要的朋友可以參考下2023-02-02
Spring事務(wù)傳播屬性和隔離級(jí)別詳細(xì)介紹
這篇文章主要介紹了Spring事務(wù)傳播屬性和隔離級(jí)別詳細(xì)介紹,同時(shí)涉及傳播行為介紹,超時(shí)設(shè)置等相關(guān)內(nèi)容,需要的朋友可以參考下。2017-09-09
spring的data派生查詢機(jī)制的實(shí)現(xiàn)
SpringData的派生查詢是一種通過方法名約定自動(dòng)生成數(shù)據(jù)庫查詢的機(jī)制,無需手動(dòng)編寫SQL或JPQL,下面就來介紹一下spring data派生查詢的實(shí)現(xiàn),感興趣的可以了解一下2025-03-03

