Spring boot 2.1 RequestMappingHandlerMapping url信息

前言

最近做项目,业务升级Spring boot的高一点的版本,发现以前mapping信息在日志不打印了,这就很难受了,毕竟看日志是一个基本日常,突然发现不能一眼看出HTTP的URL mapping了。都不知道是否成功,另外不知道那些URL是否成功提供HTTP能力,可能是为了安全考虑,Spring 5.1开始居然看不见requestmapping信息了。

1. demo

随意一个Spring boot,官方脚手架去下载即可,IDEA自动生成即可。

1.1 如何打印mapping信息

其实Spring还是会打印mapping信息,只是格式变了,另外地方变了,最主要的是日志级别变了,变为trace级别。

在application.yaml配置一下即可打印

logging:
  level:
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping: trace

打印日志如下:

Spring boot 2.1 RequestMappingHandlerMapping url信息_第1张图片

2. 原理

其实是Spring的源码作了改变,以Spring boot 2.3.5.RELEASE为例

Spring 在export HTTP的URL需要实例化bean

RequestMappingHandlerMapping

那么在哪里初始化的呢,源于Spring boot自动装配

Spring boot 2.1 RequestMappingHandlerMapping url信息_第2张图片

然后装配bean

Spring boot 2.1 RequestMappingHandlerMapping url信息_第3张图片

既然是创建bean,那么会执行

afterPropertiesSet

Spring boot 2.1 RequestMappingHandlerMapping url信息_第4张图片

然后就会判断是否是requestmapping

protected void initHandlerMethods() {
    for (String beanName : getCandidateBeanNames()) {
        if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
				processCandidateBean(beanName);
        }
    }
    handlerMethodsInitialized(getHandlerMethods());
}

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);
			}
		}
		if (beanType != null && isHandler(beanType)) {
			detectHandlerMethods(beanName);
		}
	}

有Controller 或者requestmapping

    protected boolean isHandler(Class beanType) {
		return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
				AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
	}

关键的地方

	protected void detectHandlerMethods(Object handler) {
        //获取class,但是可能是代理类
		Class handlerType = (handler instanceof String ?
				obtainApplicationContext().getType((String) handler) : handler.getClass());

		if (handlerType != null) {
            //代理类通过parent的方式获取原始实例class
			Class userType = ClassUtils.getUserClass(handlerType);
            //拿到符合条件的method
			Map methods = MethodIntrospector.selectMethods(userType,
					(MethodIntrospector.MetadataLookup) 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));
			}
			methods.forEach((method, mapping) -> {
                //AOP,就使用AOP类的方法
				Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
                //注册handler
				registerHandlerMethod(handler, invocableMethod, mapping);
			});
		}
	}

总结

Spring boot已经改变了mapping的日志打印方式,日志级别变为trace。但是原理很简单,来源于自动装配,在org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping的bean创建的时候就会拿到所有的mapping,并注册handler,此时就可以看见所有的URL mapping并打印日志,生产上不建议这样处理,毕竟Spring这样处理是有其用意的。

你可能感兴趣的:(spring,boot,spring,boot)