依据@RequestMapping原理实现自定义注解
基于最近的一个需要:客户端与服务端通过socket连贯,在socket通道里传输信息,在信息里定义一个type辨别客户端的申请类型,依据这个类型找到对应的业务解决逻辑。这不就是依据url解析对应门路找到Controller吗!于是间接开始撸@RequestMapping源码。
@RequestMapping源码剖析
间接看到 AbstractHandlerMethodMapping中afterPropertiesSet()
/** * 在初始化时检测处理程序办法 * @see #initHandlerMethods */@Overridepublic 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()
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外面查找以后对应的method
/** * 在指定的处理程序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@Nullableprotected 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;}
@Nullableprivate 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数量并打印进去。到这里咱们再来缕一缕流程:
- 获取ApplicationContext中所有被@Controller或@RequestMapping标识的bean
- 遍历bean中的method,获取bean和method的RequestMappingInfo,将两个RequestMappingInfo整合,
- 将失去RequestMappingInfo和对应的HandlerMethod存入Map中
- 失去了对应的可执行method,那么什么时候调用呢,接下来看看上面的org.springframework.web.servlet.DispatcherServlet#doDispatch 当申请过去了做的散发
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#lookupHandlerMethod
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); }}
到这里源码就全副浏览完了
尝试实现@SocketMapping
间接上代码
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@Componentpublic 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)@Documentedpublic @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@Builderpublic class SocketMappingInfo { private String name; private Method method; private String beanName;}
我这里简略实现了依据type查找对应method,@SocketMapping很多中央有待优化。后续可能持续扩大将注解增加到类上、门路解析等性能,心愿对大家有所帮忙。