基於最近的一個需求:客戶端與服務端經過socket鏈接,在socket通道里傳輸信息,在信息裏定義一個type區分客戶端的請求類型,根據這個類型找到對應的業務處理邏輯。這不就是根據url解析對應路徑找到Controller嗎!因而直接開始擼@RequestMapping源碼。java
直接看到 AbstractHandlerMethodMapping中afterPropertiesSet()ios
/** * 在初始化時檢測處理程序方法 * @see #initHandlerMethods */ @Override public void afterPropertiesSet() { initHandlerMethods(); } /** * 在ApplicationContext中掃描bean,檢測並註冊處理程序方法 * SCOPED_TARGET_NAME_PREFIX表示域代理bean的前綴 * @see #getCandidateBeanNames() * @see #processCandidateBean * @see #handlerMethodsInitialized */ protected void initHandlerMethods() { for (String beanName : getCandidateBeanNames()) { if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) { processCandidateBean(beanName); } } handlerMethodsInitialized(getHandlerMethods()); }
在properties屬性加載好以後就會初始化程序處理方法initHandlerMethods(),getCandidateBeanNames() 獲取上下文中beanName,processCandidateBean(beanName)只處理不是域代理bean的bean,而後咱們看看processCandidateBean()web
protected void processCandidateBean(String beanName) { Class<?> beanType = null; try { 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); } } // beanType != null 表示肯定具備給定名稱bean的類型 // isHandler(beanType)表示是否被@Controller和@RequestMapping標註 if (beanType != null && isHandler(beanType)) { detectHandlerMethods(beanName); } } /** * org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#isHandler * 提供實現 */ @Override protected boolean isHandler(Class<?> beanType) { return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class)); }
而後再看看detectHandlerMethods() 就是在指定的bean裏面查找當前對應的methodspring
/** * 在指定的處理程序bean中查找處理程序方法 * @param handler either a bean name or an actual handler instance * @see #getMappingForMethod */ protected void detectHandlerMethods(Object handler) { //找出當前的類型,爲了兼容能夠傳入beanName再在ApplicationContext裏查找初對應的類型 Class<?> handlerType = (handler instanceof String ? obtainApplicationContext().getType((String) handler) : handler.getClass()); if (handlerType != null) { //返回給定類的用戶定義類:一般只是給定的類,但對於CGLIB生成的子類,則返回原始類 Class<?> userType = ClassUtils.getUserClass(handlerType); //根據相關聯的元數據查找選擇給定目標類型上的方法 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)); } //將當前獲得信息便利存放到Map中,key是RequestMappingInfo,value包裝成HandlerMethod methods.forEach((method, mapping) -> { Method invocableMethod = AopUtils.selectInvocableMethod(method, userType); registerHandlerMethod(handler, invocableMethod, mapping); }); } }
/** * 使用方法和類型級別的@ {@ link RequestMapping}註解 RequestMappingInfo * @return the created RequestMappingInfo, or {@code null} if the method * does not have a {@code @RequestMapping} annotation. * @see #getCustomMethodCondition(Method) * @see #getCustomTypeCondition(Class) */ @Override @Nullable protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) { //根據method建立RequestMappingInfo->包含路徑信息,參數信息等 RequestMappingInfo info = createRequestMappingInfo(method); if (info != null) { //根據類型建立根據method建立RequestMappingInfo RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType); if (typeInfo != null) { //將類和method的RequestMappingInfo整合 info = typeInfo.combine(info); } //解析類的路徑信息 String prefix = getPathPrefix(handlerType); if (prefix != null) { info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info); } } return info; }
@Nullable private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) { RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class); RequestCondition<?> condition = (element instanceof Class ? getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element)); return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null); } 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(); }
再回到initHandlerMethods()中的handlerMethodsInitialized(),這個方法就是獲取Map中的HandlerMethod數量並打印出來。到這裏咱們再來縷一縷流程:json
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. //重點看這個方法,層層遞進找怎麼獲取到handler的,就會看到下面的方法 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()); if (asyncManager.isConcurrentHandlingStarted()) { return; } applyDefaultViewName(processedRequest, mv); mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } catch (Throwable err) { // As of 4.3, we're processing Errors thrown from handler methods as well, // making them available for @ExceptionHandler methods and other scenarios. dispatchException = new NestedServletException("Handler dispatch failed", err); } processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Throwable err) { triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", err)); } finally { if (asyncManager.isConcurrentHandlingStarted()) { // Instead of postHandle and afterCompletion if (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else { // Clean up any resources used by a multipart request. if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } } }
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#lookupHandlerMethodmvc
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { List<Match> matches = new ArrayList<>(); //當前這個this.mappingRegistry就是咱們以前把信息存進去的Map List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath); if (directPathMatches != null) { addMatchingMappings(directPathMatches, matches, request); } if (matches.isEmpty()) { // No choice but to go through all mappings... addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request); } if (!matches.isEmpty()) { Match bestMatch = matches.get(0); if (matches.size() > 1) { Comparator<Match> comparator = new MatchComparator(getMappingComparator(request)); matches.sort(comparator); bestMatch = matches.get(0); 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); return bestMatch.handlerMethod; } else { return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request); } }
到這裏源碼就所有閱讀完了app
直接上代碼socket
package com.cmge.handler; import com.alibaba.fastjson.JSONObject; import com.cmge.annotation.SocketMapping; import com.cmge.controller.BaseController; import com.cmge.info.SocketMappingInfo; import lombok.extern.slf4j.Slf4j; import org.springframework.aop.support.AopUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.core.MethodIntrospector; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.lang.Nullable; import org.springframework.stereotype.Component; import org.springframework.util.ClassUtils; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.LinkedHashMap; import java.util.Map; /** * @author jasongaoj * @version 1.0.0 * @Description 請求業務處理類 * @createTime 2020年11月26日 17:49:00 */ @Slf4j @Component public class RequestMappingHandler { private final Map<String, SocketMappingInfo> mappingLookup = new LinkedHashMap<>(); @Autowired private ApplicationContext applicationContext; /** * 解析消息,分發請求 * @param msg * @return */ public String doDispatchMessage(String msg) throws InvocationTargetException, IllegalAccessException { JSONObject msgJSON=JSONObject.parseObject(msg); String mapping=msgJSON.getString("type"); SocketMappingInfo socketMappingInfo=mappingLookup.get(mapping); if(socketMappingInfo!=null){ return (String) socketMappingInfo.getMethod().invoke(applicationContext.getBean(socketMappingInfo.getBeanName()),msg); } return null; } public boolean parseParam(JSONObject jsonObject){ String mapping=jsonObject.getString("type"); if(mapping.equals("SYS")){ log.trace("過濾網關心跳檢測。。。"); } return true; } /** * 註冊全部的mapping */ public void registerSocketHandlerMapping(){ String[] beanNames=applicationContext.getBeanNamesForType(BaseController.class); for (String beanName:beanNames) { processCandidateBean(beanName); } } /** * 肯定指定候選bean的類型並調用 * @param beanName */ private void processCandidateBean(String beanName){ Class<?> beanType = null; try { beanType = applicationContext.getType(beanName); } catch (Throwable ex) { // An unresolvable bean type, probably from a lazy bean - let's ignore it. log.trace("Could not resolve type for bean '" + beanName + "'", ex); } //若是當前bean存在實例,則檢測當前可執行的method if (beanType != null ) { detectHandlerMethods(beanType,beanName); } } /** * 獲取當前mapping對應的method,將解析的method註冊到mappingLookup * (key,value)->(@SocketMapping.value(),invokeMethod()) * @param beanType * @param beanName */ private void detectHandlerMethods(Class<?> beanType,String beanName){ Class<?> userType = ClassUtils.getUserClass(beanType); Map<Method, SocketMappingInfo> methods = MethodIntrospector.selectMethods(userType, (MethodIntrospector.MetadataLookup<SocketMappingInfo>) method -> { try { return createRequestMappingInfo(method); } catch (Throwable ex) { throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, ex); } }); methods.forEach(((method, socketMappingInfo) -> { Method invocableMethod = AopUtils.selectInvocableMethod(method, userType); socketMappingInfo.setMethod(invocableMethod); socketMappingInfo.setBeanName(beanName); mappingLookup.put(socketMappingInfo.getName(),socketMappingInfo); })); } /** * 建立mapping信息 * @param element * @return */ @Nullable private SocketMappingInfo createRequestMappingInfo(AnnotatedElement element) { SocketMapping socketMapping = AnnotatedElementUtils.findMergedAnnotation(element, SocketMapping.class); SocketMappingInfo socketMappingInfo=SocketMappingInfo.builder() .name(socketMapping.value()) .build(); return socketMappingInfo; } } package com.cmge.annotation; import org.springframework.stereotype.Component; import java.lang.annotation.*; /** * @author jasongaoj * @version 1.0.0 * @Description socket消息中的type實現請求分發 * @createTime 2020年11月27日 10:03:00 */ @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface SocketMapping { String value() default ""; } package com.cmge.info; import lombok.*; import java.lang.reflect.Method; /** * @author jasongaoj * @version 1.0.0 * @Description SocketMapping信息 * @createTime 2020年11月27日 11:20:00 */ @Data @AllArgsConstructor @NoArgsConstructor @Builder public class SocketMappingInfo { private String name; private Method method; private String beanName; }
我這裏簡單實現了根據type查找對應method,@SocketMapping不少地方有待優化。後續可能繼續擴展將註解添加到類上、路徑解析等功能,但願對你們有所幫助。async