核心:得到应用上下文中存在的全部bean后依次遍历,分析每一个目标handler & 目标方法存在的注解@RequestMapping,将其相关属性封装为实例RequestMappingInfo。最终将 uri & handler 之间的映射关系维护在类AbstractHandlerMethodMapping
中的内部类RequestMappingInfo
中。
利用RequestMappingHandlerMapping
之InitializingBean
接口特性来完成请求 uri & handler 之间的映射关系。具体详情参考其父类AbstractHandlerMethodMapping
实现其功能,如下:
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
//内部类
private final MappingRegistry mappingRegistry = new MappingRegistry();
protected void initHandlerMethods() {
for (String beanName : getCandidateBeanNames()) {//从应用上下文中获取全部的bean
processCandidateBean(beanName);
}
}
protected void processCandidateBean(String beanName) {
Class<?> beanType = obtainApplicationContext().getType(beanName);
// 判断是否为Handler的条件为:是否存在注解Controller 或者 RequestMapping
if (beanType != null && isHandler(beanType)) {
detectHandlerMethods(beanName);
}
}
protected void detectHandlerMethods(Object handler) {//handler为String类型的beanName
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
Class<?> userType = ClassUtils.getUserClass(handlerType);
// 集合methods其key:反射中Method类。value:RequestMappingInfo
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
// 调用具体的实现类,例如RequestMappingHandlerMapping
return getMappingForMethod(method, userType);
});
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
this.mappingRegistry.register(mapping, handler, method);
}
}
解析目标类 & 目标方法之@RequestMapping注解相关属性。
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping{
@Override
@Nullable
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
//将目标方法存在的@RequestMapping注解相关属性封装为RequestMappingInfo
RequestMappingInfo info = createRequestMappingInfo(method);
if (info != null) {
//如果目标handler也存在@RequestMapping注解,则也封装为RequestMappingInfo
RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
if (typeInfo != null) {
// 将目标方法@RequestMapping相关属性与目标handler之@RequestMapping相关属性合并起来
info = typeInfo.combine(info);
}
String prefix = getPathPrefix(handlerType);//获取目标handler之url前缀
if (prefix != null) {
info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
}
}
return info;
}
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
return (requestMapping != null ? createRequestMappingInfo(requestMapping, null) : null);
}
protected RequestMappingInfo createRequestMappingInfo(RequestMapping requestMapping,RequestCondition condition) {
// 获取某个具体目标方法其@RequestMapping注解相关属性,最终封装为RequestMappingInfo
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());
return builder.options(this.config).build();
}
}
public abstract class AbstractHandlerMethodMapping<T> {
class MappingRegistry {
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
//集合mappingLookup之key:RequestMappingInfo。value:HandlerMethod
private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
public void register(T mapping, Object handler, Method method) {
...
// 目标类的目标方法最终封装为HandlerMethod handler实为目标bean的beanName
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
this.mappingLookup.put(mapping, handlerMethod);
List<String> directUrls = getDirectUrls(mapping);
for (String url : directUrls) {
this.urlLookup.add(url, mapping);
}
...
this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
}
}
}
SpringMVC利用请求url获取目标handler。
public class DispatcherServlet extends FrameworkServlet {
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
// 围绕 RequestMappingHandlerMapping 展开
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
}
public abstract class AbstractHandlerMethodMapping<T> implements InitializingBean {
private final MappingRegistry mappingRegistry = new MappingRegistry();
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request){
// 获取requestUri
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
// 从 mappingRegistry 获取目标handler
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) {
List<Match> matches = new ArrayList<>();
// 利用 lookupPath 从MappingRegistry相关属性中获取目标handler之T
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
// 通过章节1得知,返回的类为 RequestMappingInfo,即@RequestMapping注解的相关属性
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);
}
// 如果局部matches中元素为空,说明请求头校验失败
if (!matches.isEmpty()) {
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
matches.sort(comparator);
Match bestMatch = matches.get(0);
...
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);
}
}
}
请求头相关参数校验失败抛出的异常包括:HttpRequestMethodNotSupportedException
、HttpMediaTypeNotSupportedException
、HttpMediaTypeNotAcceptableException
、UnsatisfiedServletRequestParameterException
。
但是这些类型的异常好像不能被全局拦截器拦截处理。
核心就是校验请求request中相关属性跟RequestMappingInfo中属性是否匹配。
public abstract class AbstractHandlerMethodMapping<T> implements InitializingBean {
private void addMatchingMappings(Collection mappings, List matches, HttpServletRequest request) {
for (T mapping : mappings) {
T match = getMatchingMapping(mapping, request);
if (match != null) {//正常请求校验都通过,最终返回重新包装后的RequestMappingInfo
matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
}
}
}
}
public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMethodMapping {
@Override
protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) {
return info.getMatchingCondition(request);
}
}
该类中的相关属性是对 注解@RequestMapping之字段属性的封装。
public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {
public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
//请求方式: 校验Method属性,即是否为post or get 等相对应的请求方式
RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request);
if (methods == null) {
return null;
}
//请求参数
ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request);
if (params == null) {
return null;
}
//consumer参数:请求头中contentType是否一致。
HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request);
if (headers == null) {
return null;
}
//producer参数:请求头中Accept是否一致
ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request);
if (consumes == null) {
return null;
}
ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request);
if (produces == null) {
return null;
}
PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(request);
if (patterns == null) {
return null;
}
RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request);
if (custom == null) {
return null;
}
return new RequestMappingInfo(this.name, patterns,
methods, params, headers, consumes, produces, custom.getCondition());
}
}