@RequestMapping是如何运行的?

1,784 阅读5分钟

@RequestMapping是如何运行的?

博客索引

最近在写一个权限框架,想着怎么获取项目里面所有的url映射? 先说结论是怎么获取的,后面详细介绍。

    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运行的流程,它为什么可以通过一个url就可以执行到对应的方法呢? 这肯定是初始化的时候,将Controller里面的带有@RequestMapping注解的方法与uri保存在集合中,然后执行的时候,根据请求的url到集合里面去找到对应的方法, 然后通过反射执行方法,完成调用。

@RequestMapping的初始化

RequestMappingHandlerMapping:首先从该类的的afterPropertiesSet入手:

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父类的初始化方法:

@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值存在这个对象中。

	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。

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)

  // 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();
			}
		}