最近做项目,业务升级Spring boot的高一点的版本,发现以前mapping信息在日志不打印了,这就很难受了,毕竟看日志是一个基本日常,突然发现不能一眼看出HTTP的URL mapping了。都不知道是否成功,另外不知道那些URL是否成功提供HTTP能力,可能是为了安全考虑,Spring 5.1开始居然看不见requestmapping信息了。
随意一个Spring boot,官方脚手架去下载即可,IDEA自动生成即可。
其实Spring还是会打印mapping信息,只是格式变了,另外地方变了,最主要的是日志级别变了,变为trace级别。
在application.yaml配置一下即可打印
logging:
level:
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping: trace
打印日志如下:
其实是Spring的源码作了改变,以Spring boot 2.3.5.RELEASE为例
Spring 在export HTTP的URL需要实例化bean
RequestMappingHandlerMapping
那么在哪里初始化的呢,源于Spring boot自动装配
然后装配bean
既然是创建bean,那么会执行
afterPropertiesSet
然后就会判断是否是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这样处理是有其用意的。