springboot webmvc初始化:一个登陆异常引出的话题

时间:2022-01-04 00:18:00

1.场景复现:

一个登陆异常引出的问题。

我们通过精心组织组件扫描的方式,来装配不同的子模块,形成一个可运行的应用;

在载入某个子模块后,我们发现应用虽然正常启动,但尝试登陆的时候,出现一个很异常的异常,部分stacktrace如下:

processing failed; nested exception is java.lang.IllegalStateException: No primary or default constructor found for interface org.springframework.mobile.device.Device] with root cause

java.lang.NoSuchMethodException: org.springframework.mobile.device.Device.<init>()
at java.lang.Class.getConstructor0(Class.java:3082) ~[na:1.8.0_171]
at java.lang.Class.getDeclaredConstructor(Class.java:2178) ~[na:1.8.0_171]
at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.createAttribute(ModelAttributeMethodProcessor.java:209) ~[spring-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor.createAttribute(ServletModelAttributeMethodProcessor.java:84) ~[spring-webmvc-5.0.7.RELEASE.jar:5.0.7.RELEASE]
at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:132) ~[spring-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:124) ~[spring-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]

异常栈部分调用关系如下:

springboot webmvc初始化:一个登陆异常引出的话题

如果移除子模块,应用启动后,同一个入口会触发的正常调用栈如下:

springboot webmvc初始化:一个登陆异常引出的话题

换句话说,异常情况和正常情况,走了不同的处理链。

1.我们初步怀疑是包冲突,但验证后排除掉这个问题;

2.然后继续通过调试代码,分析发现加入子模块后,启动的

HandlerMethodArgumentResolver 实例有27个;

而不加入子模块,启动的

HandlerMethodArgumentResolver 实例有33个,多了几个resolver,其中包括DeviceHandlerMethodArgumentResolver,看起来找到好的线索了;

3.根据线索,怀疑是配置问题,首先怀疑的是安全方面的配置问题;但一直测试分析没有得到结果;

4.后续,用排除法,发现此模块中去掉一个定义的配置类,问题就可以得到解决,这个配置如下::

Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {

...

}

2.经过所有线索的梳理,这个问题产生的内在机制比较明确了:

 

HandlerMethodArgumentResolver

源码注释:
Strategy interface for resolving method parameters into argument values in the context of a given request.

简单可以理解为:解析方法参数的策略接口;通俗的理解为,(controller的)参数解析(构造)转换按这个接口来实现;

WebMvcConfigurationSupport

源码注释:

This is the main class providing the configuration behind the MVC Java config.

其中有一个方法:

@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {

简而言之,DeviceHandlerMethodArgumentResolver 实例是靠

WebMvcConfigurationSupport 提供的机制(模板函数),加载进容器处理链的;

DelegatingWebMvcConfiguration

实际上,spring 依靠

DelegatingWebMvcConfiguration 自动检测并代理实现所有的自定义配置
/**
* A subclass of {@code WebMvcConfigurationSupport} that detects and delegates
* to all beans of type {@link WebMvcConfigurer} allowing them to customize the
* configuration provided by {@code WebMvcConfigurationSupport}. This is the
* class actually imported by {@link EnableWebMvc @EnableWebMvc}.
*
* @author Rossen Stoyanchev
* @since 3.1
*/
@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

WebMvcAutoConfiguration

DelegatingWebMvcConfiguration 的实例创建,是通过下面这个类
WebMvcAutoConfiguration 导入,但是有个条件:当容器内不存在 WebMvcConfigurationSupport 的实例时
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {


所以,当我们自己子模块的代码,有这个自定义配置时

@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {

系统默认的webmvc 配置行为将会被覆盖