基于最近的一个需求:客户端与服务端经过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