spring-mvc-handlerMapping 是怎麼存放我們的請求路徑的-原始碼
阿新 • • 發佈:2020-06-24
不要問我閱讀spring原始碼有什麼用,問就是沒有用,只是讓我自己使用spring的過程中自信點!
相關文章
相關連結
- spring-mvc-handlerMapping 是怎麼存放我們的請求路徑的-原始碼
- springmvc-執行流程
- spring-mvc 時如何選擇 messageConverter
- springMVC-Interceptor-原始碼分析
對映關係是在哪裡拿到的
首先有幾個類
- HandlerExecutionChain,這個是執行鏈
- HandlerMethod 這個就是我們常用的請求的封裝(也是本文的目標)
- HandlerAdapter 介面卡(處理請求的就是從它開始)
- HandlerExecutionChain.handler 屬性 就是 HandlerMethod
- HandlerAdapter 中最後執行的也是 ServletInvocableHandlerMethod 也是從 HandlerMethod 封裝的
說明: 本文只對 HandlerMethod 的獲取進行分析,不對mvc的執行流程分析,所有的一起原始碼都是針對常見的請求(@Controller 和 @RequestMapping)
HandlerMethod的獲取
上面已經知道了最後執行的是HandlerExecutionChain.handler,那就看看這個屬性是如何被賦值的
方法入口為
protected void doDispatch(HttpServletRequest request,HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
//就是在這個地方獲取的 handlerExecutionChain,也是在這裡獲取的 handlerMethod
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest,response);
return;
}
// Determine handler adapter for the current request.
//獲取介面卡
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header,if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request,mappedHandler.getHandler());
if (new ServletWebRequest(request,response).checkNotModified(lastModified) && isGet) {
return;
}
}
// 攔截器的執行
if (!mappedHandler.applyPreHandle(processedRequest,response)) {
return;
}
// Actually invoke the handler.
//執行請求
mv = ha.handle(processedRequest,response,mappedHandler.getHandler());
.....................................
}
複製程式碼
上述程式碼 刪除無關程式碼,繼續往下:
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
//文章開頭說了 handlerMethod 是在 handlerExceutionChain 中,所以在這個程式碼裡
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
複製程式碼
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//就是在這裡獲取的 handlerMethod 也就是請求的對映關係
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
//獲取 執行鏈 把 handlerMethod 放入 HandlerExecutionChain
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler,request);
if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
}
else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
logger.debug("Mapped to " + executionChain.getHandler());
}
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler,request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request,executionChain,config);
}
return executionChain;
}
複製程式碼
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
//根據request 獲取 請求路徑 裡面東西還挺多,不多也就是獲取路徑的
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
this.mappingRegistry.acquireReadLock();
try {
//獲取 handlerMethod
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath,request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
複製程式碼
@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath,HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
//這裡根據請求路徑獲取requsetMappingInfo 格式為 [{GET /xml,produces [application/xml]}]
//請求方式,請求路徑,響應型別 注意 現在這個類 是requestMappingHandlerMapping 的一個父類
// 說明 這也資訊有可能是在 requestMappingHandlerMapping 初始化的時候放進去的.
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
//獲取 Match 的方法 Match 中包含 HandlerMethod
addMatchingMappings(directPathMatches,matches,request);
}
if (matches.isEmpty()) {
// No choice but to go through all mappings...
addMatchingMappings(this.mappingRegistry.getMappings().keySet(),request);
}
if (!matches.isEmpty()) {
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
matches.sort(comparator);
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
if (logger.isTraceEnabled()) {
logger.trace(matches.size() + " matching mappings: " + matches);
}
if (CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch,secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
String uri = request.getRequestURI();
throw new IllegalStateException(
"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + "," + m2 + "}");
}
}
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE,bestMatch.handlerMethod);
handleMatch(bestMatch.mapping,lookupPath,request);
// 發現返回的 handlerMethod 是 Match 屬性 所以我們關注 matches 就行了
return bestMatch.handlerMethod;
}
else {
return handleNoMatch(this.mappingRegistry.getMappings().keySet(),request);
}
}
複製程式碼
private void addMatchingMappings(Collection<T> mappings,List<Match> matches,HttpServletRequest request) {
for (T mapping : mappings) {
//這裡又去獲取了一次 requestMappingInfo 我以為不知道為啥又去獲取了一次
T match = getMatchingMapping(mapping,request);
if (match != null) {
//這裡建立了Match 點進去發現傳了兩個引數 一個是requestMapping 一個是 HandlerMethod
//現在主要看下 this.mappingRegistry.getMappings().get(mapping))
matches.add(new Match(match,this.mappingRegistry.getMappings().get(mapping)));
}
}
}
複製程式碼
public Map<T,HandlerMethod> getMappings() {
return this.mappingLookup;
}
//到這裡可以看到了 HandlerMethod 是從mappingLookup獲取的
private final Map<T,HandlerMethod> mappingLookup = new LinkedHashMap<>();
複製程式碼
下面就要mappingLookup是什麼時候被賦值的
mappingLookup 是 MappingRegistry 的屬性 MappingRegistry 是 AbstractHandlerMethodMapping的內部類
對映關係是在哪裡被載入進去的
思路: 在MappingRegistry.put() 的地方debug,觀察執行棧
重要執行節點:requestMappingHandlerMapping初始化-> afterPropertiesSet()
- requestMappingHandlerMapping初始化就不看了,是spring-ioc 的東西,以後會講
- requestMappingHandlerMapping的父類AbstractHandlerMethodMapping實現了InitializingBean介面
- spring bean 初始化之後 會執行 @PostConstruct 註解的 方法 實現了InitializingBean介面 的afterPropertiesSet()方法,我們這裡用到的是InitializingBean
貼程式碼如下:
protected void invokeInitMethods(String beanName,final Object bean,@Nullable RootBeanDefinition mbd)
throws Throwable {
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isTraceEnabled()) {
logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
((InitializingBean) bean).afterPropertiesSet();
return null;
},getAccessControlContext());
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
//這裡就是入口
((InitializingBean) bean).afterPropertiesSet();
}
}
if (mbd != null && bean.getClass() != NullBean.class) {
String initMethodName = mbd.getInitMethodName();
if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
invokeCustomInitMethod(beanName,bean,mbd);
}
}
}
複製程式碼
@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());
//看這裡,呼叫了父類的 afterPropertiesSet();
super.afterPropertiesSet();();
}
複製程式碼
@Override
public void afterPropertiesSet() {
//這裡
initHandlerMethods();
}
//這裡
protected void initHandlerMethods() {
//getCandidateBeanNames() 應該是獲取了所有的beanName
for (String beanName : getCandidateBeanNames()) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
//就是這裡
processCandidateBean(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
//根絕beanName處理的
protected void processCandidateBean(String beanName) {
Class<?> beanType = null;
try {
//獲取當前bean的class 型別
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);
}
}
//這裡 重點 isHandler() 判斷是否需要處理
if (beanType != null && isHandler(beanType)) {
//假如是的話,繼續 (重點)
detectHandlerMethods(beanName);
}
}
// 看到了,條件就是 當前類是否存在 @Controller @RequestMapping 註解
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType,Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType,RequestMapping.class));
}
複製程式碼
下面的程式碼就是最後了,注意看! 下面的程式碼比較繞,用的是java8的Lambda 表示式,假如對java8比較熟,看這段程式碼是無壓力的 先把這幾個方法的程式碼都貼出來,如下:
protected void detectHandlerMethods(Object handler) {
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
//獲取當前contorller 型別
Class<?> userType = ClassUtils.getUserClass(handlerType);
// 第一步 就是這裡 執行selectMethods 這個方法 傳遞了一個行為(lambda表示式)
Map<Method,T> methods = MethodIntrospector.selectMethods(userType,(MethodIntrospector.MetadataLookup<T>) method -> {
try {
//第六步
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));
}
//第七步
methods.forEach((method,mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method,userType);
registerHandlerMethod(handler,invocableMethod,mapping);
});
}
}
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;
//判斷是否存在代理
if (!Proxy.isProxyClass(targetType)) {
specificHandlerType = ClassUtils.getUserClass(targetType);
handlerTypes.add(specificHandlerType);
}
handlerTypes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetType));
//迴圈 第二步 目標 controller 正常情況就一個元素
for (Class<?> currentHandlerType : handlerTypes) {
final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);
// 呼叫 doWithMethods 第三步
ReflectionUtils.doWithMethods(currentHandlerType,method -> {
//第五步 獲取到了 method
Method specificMethod = ClassUtils.getMostSpecificMethod(method,targetClass);
T result = metadataLookup.inspect(specificMethod);
if (result != null) {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {
methodMap.put(specificMethod,result);
}
}
},ReflectionUtils.USER_DECLARED_METHODS);
}
return methodMap;
}
public static void doWithMethods(Class<?> clazz,MethodCallback mc,@Nullable MethodFilter mf) {
// Keep backing up the inheritance hierarchy.
//獲取所有的方法
Method[] methods = getDeclaredMethods(clazz);
for (Method method : methods) {
if (mf != null && !mf.matches(method)) {
continue;
}
try {
//執行 selectMethods 方法中的 MethodFilter mf 第四步
mc.doWith(method);
}
catch (IllegalAccessException ex) {
throw new IllegalStateException("Not allowed to access method '" + method.getName() + "': " + ex);
}
}
if (clazz.getSuperclass() != null) {
doWithMethods(clazz.getSuperclass(),mc,mf);
}
else if (clazz.isInterface()) {
for (Class<?> superIfc : clazz.getInterfaces()) {
doWithMethods(superIfc,mf);
}
}
}
//儲存對應關係入口
protected void registerHandlerMethod(Object handler,Method method,T mapping) {
this.mappingRegistry.register(mapping,handler,method);
}
//結束了
public void register(T mapping,Object handler,Method method) {
this.readWriteLock.writeLock().lock();
try {
HandlerMethod handlerMethod = createHandlerMethod(handler,method);
assertUniqueMethodMapping(handlerMethod,mapping);
//儲存到 mappingLookup
this.mappingLookup.put(mapping,handlerMethod);
List<String> directUrls = getDirectUrls(mapping);
for (String url : directUrls) {
//儲存路徑到urlLookup
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();
}
}
複製程式碼
總結:
- spring-ioc 初始化 requestMappingHandlerMapping 的時候 把對應關係 存放在mappingLookup
- 執行的時候 根絕請求路徑 查詢urlLookup,再根據 urlLookup 的val 當做key 去 mappingLookup 中查詢