最近在写一个权限框架,想着怎么获取项目里面全部的url映射? 先说结论是怎么获取的,后面详细介绍。java
Map<RequestMappingInfo, HandlerMethod> map = RequestMappingHandlerMapping.getHandlerMethods();
// 获取全部的key
Set<RequestMappingInfo> requestMappingInfos = handlerMethods.keySet();
// 获取全部的mapping
Set<String> mappings =new LinkedHashSet<>();
for (RequestMappingInfo info : requestMappingInfos) {
PatternsRequestCondition patternsCondition = info.getPatternsCondition();
for (String mapping : patternsCondition.getPatterns()){
mappings.add(mapping);
}
}
复制代码
而后就想起来MVC运行的流程,它为何能够经过一个uri就能够执行到对应的方法呢? 这确定是初始化的时候,将Controller里面的带有@RequestMapping
注解的方法与uri保存在集合中,而后执行的时候,根据请求的uri到集合里面去找到对应的方法, 而后经过反射执行方法,完成调用。web
RequestMappingHandlerMapping
:首先从该类的的afterPropertiesSet
入手:spring
public void afterPropertiesSet() {
this.config = new RequestMappingInfo.BuilderConfiguration();
this.config.setUrlPathHelper(getUrlPathHelper());
this.config.setPathMatcher(getPathMatcher());
this.config.setSuffixPatternMatch(useSuffixPatternMatch());
this.config.setTrailingSlashMatch(useTrailingSlashMatch());
this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());
this.config.setContentNegotiationManager(getContentNegotiationManager());
//调用了父类的afterPropertiesSet
super.afterPropertiesSet();
}
//判断bean是否带有@Controller或者@RequestMapping注解
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
复制代码
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#afterPropertiesSet
父类的初始化方法:bash
@Override
public void afterPropertiesSet() {
initHandlerMethods();
}
protected void initHandlerMethods() {
//getCandidateBeanNames()获取全部注册的bean
for (String beanName : getCandidateBeanNames()) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
// 处理候选bean
processCandidateBean(beanName);
}
}
// 打印日志,输出日志handlerMethods的数量
handlerMethodsInitialized(getHandlerMethods());
}
protected void processCandidateBean(String beanName) {
Class<?> beanType = null;
try {
// 获取bean的类型
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);
}
}
// 从候选bean中选出知足isHandler(beanType)的bean,判断bean是否带有@Controller或者@RequestMapping注解
// isHandler(Class<?> beanType)在该类中未实现,具体实现是在RequestMappingHandlerMapping中,上面贴出了代码
if (beanType != null && isHandler(beanType)) {
detectHandlerMethods(beanName);
}
}
// 在指定的handler中寻找被@RequestMapping标记的methods
// 这里的handler能够理解成咱们常说的Controller
protected void detectHandlerMethods(Object handler) {
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
// 返回自己的类,若是该Class是CGLB代理的,那么就返回它的父类
Class<?> userType = ClassUtils.getUserClass(handlerType);
// 基于关联元数据的查找,选择给定目标相似上的方法
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
// 选择被@RequestMapping标记的方法,这里返回的是RequestMappingInfo对象,mapping也被存放在该对象中
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));
}
methods.forEach((method, mapping) -> {
// 返回给定一个Class上的可调用的方法
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
// 注册请求映射
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
复制代码
RequestMappingInfo
,简单介绍一下,这个也是下面注册方法里面传递的mapping,也就是说咱们所须要的mapping值存在这个对象中。app
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
// 获取带有@RequestMapping注解方法上面的path值
RequestMappingInfo info = createRequestMappingInfo(method);
if (info != null) {
//获取方法对于类上@RequestMapping注解方法上面的path值
RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
if (typeInfo != null) {
// 拼接起来
info = typeInfo.combine(info);
}
String prefix = getPathPrefix(handlerType);
if (prefix != null) {
info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
}
}
return info;
}
protected RequestMappingInfo createRequestMappingInfo(
RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {
RequestMappingInfo.Builder builder = RequestMappingInfo
// 能够看到咱们path被赋值到paths中
.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);
}
// 看一下这个builder,将path属性转移到哪里?
return builder.options(this.config).build();
}
public RequestMappingInfo build() {
ContentNegotiationManager manager = this.options.getContentNegotiationManager();
// 能够看到path值变成PatternsRequestCondition的一个属性patterns;,这里代码比较简单,我就知道说结论了
PatternsRequestCondition patternsCondition = new PatternsRequestCondition(
this.paths, this.options.getUrlPathHelper(), this.options.getPathMatcher(),
this.options.useSuffixPatternMatch(), this.options.useTrailingSlashMatch(),
this.options.getFileExtensions());
// 而PatternsRequestCondition又是RequestMappingInfo的一个属性,因此获取mapping的方法找到了
// 对应文章开头的方法获取全部的mapping
return new RequestMappingInfo(this.mappingName, patternsCondition,
new RequestMethodsRequestCondition(this.methods),
new ParamsRequestCondition(this.params),
new HeadersRequestCondition(this.headers),
new ConsumesRequestCondition(this.consumes, this.headers),
new ProducesRequestCondition(this.produces, this.headers, manager),
this.customCondition);
}
复制代码
介绍这个registerHandlerMethod(Object,Method,Mapping)
方法以前,先介绍几个类HandlerMethod
,看名字就知道是handle与method的结合体,这个结构保存了handle与对应的method。cors
public class HandlerMethod {
// handle
private final Object bean;
@Nullable
private final BeanFactory beanFactory;
// handle Class
private final Class<?> beanType;
// 方法
private final Method method;
private final Method bridgedMethod;
private final MethodParameter[] parameters;
...其余几个不重要的忽略
复制代码
MappingRegistry
是一个内部类,里面有几个关键成员变量,来存放全部的mapping与方法框架
class MappingRegistry {
// 保存的是mapping与MappingRegistration关系。MappingRegistration
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
// 保存的是mapping与HandlerMethod的映射关系
private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
// 保存的是url与mapping的映射关系,原本这个很关键,可是没有提供对外的方法,因此想要获取mapping,仍是得从RequestMappingInfo获取
private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
// 保存的是名字与List<HandlerMethod>的关系
private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
// // 保存的是HandlerMethod与CorsConfiguration的关系
private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
复制代码
咱们继续来介绍核心方法`registerHandlerMethod(Object,Method,Mapping)ide
// mappingRegistry是AbstractHandlerMethodMapping的一个抽象类,下面会介绍
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
this.mappingRegistry.register(mapping, handler, method);
}
public void register(T mapping, Object handler, Method method) {
// Assert that the handler method is not a suspending one.
if (KotlinDetector.isKotlinType(method.getDeclaringClass())) {
Class<?>[] parameterTypes = method.getParameterTypes();
if ((parameterTypes.length > 0) && "kotlin.coroutines.Continuation".equals(parameterTypes[parameterTypes.length - 1].getName())) {
throw new IllegalStateException("Unsupported suspending handler method detected: " + method);
}
}
// 上读写锁
this.readWriteLock.writeLock().lock();
try {
// 建立根据handle与method建立HandlerMethod,也就是创建起了Controller与对应执行方法的关系
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
//校验
validateMethodMapping(handlerMethod, mapping);
// 将mapping与handlerMethod存入map
this.mappingLookup.put(mapping, handlerMethod);
List<String> directUrls = getDirectUrls(mapping);
for (String url : directUrls) {
将url与mapping存入urlLookup中
this.urlLookup.add(url, mapping);
}
String name = null;
if (getNamingStrategy() != null) {
name = getNamingStrategy().getName(handlerMethod, mapping);
addMappingName(name, handlerMethod);
}
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
this.corsLookup.put(handlerMethod, corsConfig);
}
this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
}
finally {
this.readWriteLock.writeLock().unlock();
}
}
复制代码