依据@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数量并打印进去。到这里咱们再来缕一缕流程:

  1. 获取ApplicationContext中所有被@Controller或@RequestMapping标识的bean
  2. 遍历bean中的method,获取bean和method的RequestMappingInfo,将两个RequestMappingInfo整合,
  3. 将失去RequestMappingInfo和对应的HandlerMethod存入Map中
  4. 失去了对应的可执行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很多中央有待优化。后续可能持续扩大将注解增加到类上、门路解析等性能,心愿对大家有所帮忙。