依据 @RequestMapping 原理实现自定义注解
基于最近的一个需要:客户端与服务端通过 socket 连贯,在 socket 通道里传输信息,在信息里定义一个 type 辨别客户端的申请类型,依据这个类型找到对应的业务解决逻辑。这不就是依据 url 解析对应门路找到 Controller 吗!于是间接开始撸 @RequestMapping 源码。
@RequestMapping 源码剖析
间接看到 AbstractHandlerMethodMapping 中 afterPropertiesSet()
/**
* 在初始化时检测处理程序办法
* @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()
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
@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 数量并打印进去。到这里咱们再来缕一缕流程:
- 获取 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
@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 很多中央有待优化。后续可能持续扩大将注解增加到类上、门路解析等性能,心愿对大家有所帮忙。