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]

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

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

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

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 配置行为将会被覆盖

springboot webmvc初始化:一个登陆异常引出的话题的更多相关文章

  1. 基于SpringBoot如何实现一个点赞功能?

    基于SpringBoot如何实现一个点赞功能? 解析: 基于 SpringCloud, 用户发起点赞.取消点赞后先存入 Redis 中,再每隔两小时从 Redis 读取点赞数据写入数据库中做持久化存储 ...

  2. ZeroMQ接口函数之 :zmq_msg_init - 初始化一个空的ZMQ消息结构

    ZeroMQ 官方地址 :http://api.zeromq.org/4-1:zmq_msg_init zmq_msg_init(3) ØMQ Manual - ØMQ/3.2.5 Name zmq_ ...

  3. ZeroMQ接口函数之 :zmq_msg_init_data - 从一个指定的存储空间中初始化一个ZMQ消息对象的数据

    ZeroMQ 官方地址 :http://api.zeromq.org/4-1:zmq_msg_init_data zmq_msg_init_data(3) ØMQ Manual - ØMQ/3.2.5 ...

  4. [Android]ViewPager如何只初始化一个页面

    使用过ViewPager的应该都知道,ViewPager的setoffscreenpagelimit()方法,使用该方法可以设置在ViewPager滑动时,左右两侧各保存多少个页面,那我们直接设置se ...

  5. 如何初始化一个iOS原生地图

    /** 初始化一个mapView  需导入 #import <MapKit/MapKit.h> - returns: 返回一个mapView对象 */ mapView = [[MKMapV ...

  6. 标准程序员系列-Github篇-初始化一个代码仓库

    下面将一步步介绍怎样使用GitHub来初始化一个项目的版本控制仓库: 1. 到GitHub上注册自己的账号:https://github.com/ 2. 创建第一个代码仓库一个仓库相当于一个项目的代码 ...

  7. @Required 注释应用于 bean 属性的 setter 方法,它表明受影响的 bean 属性在配置时必须放在 XML 配置文件中,否则容器就会抛出一个 BeanInitializationException 异常。

    @Required 注释应用于 bean 属性的 setter 方法,它表明受影响的 bean 属性在配置时必须放在 XML 配置文件中,否则容器就会抛出一个 BeanInitializationEx ...

  8. 第5章 不要让线程成为脱缰的野马(Keeping your Threads on Leash) ----初始化一个线程

    使用线程的一个常见问题就是如何能够在一个线程开始运行之前,适当地将它初始化.初始化最常见的理由就是为了调整优先权.另一个理由是为了在SMP 系统中设定线程比较喜欢的 CPU.第10 章谈到 MFC 时 ...

  9. tkinter如何设置界面消失 当制作一个登陆界面时,登陆成功之后,如何让登陆界面本身消失

    tkinter如何设置界面消失 当制作一个登陆界面时,登陆成功之后,如何让登陆界面本身消失 if querySQL.checkAll():#用户名和密码都输入正确 self.root.withdraw ...

随机推荐

  1. Java处理对象

    1.打印对象和toString 方法     toString() 方法是Object 类的一个实例方法,所有的Java类都是Object类的子类,因此所有的Java对象都是具有toString()方 ...

  2. oracle中新建用户和赋予权限

    1.新建个用户 create user xxxxx(用户名) identified by "密码" alert user 用户名 identified by “新密码” --修改用 ...

  3. STM32时钟

    https://blog.csdn.net/qq_29350001/article/details/81409693 这是个大佬讲的 F429有5个时钟源,HSI,HSE,LSI,LSE;PLL; 对 ...

  4. [IoC容器Unity]第三回:依赖注入

    1.引言 上节介绍了,Unity的Lifetime Managers生命周期,Unity具体实现依赖注入包含构造函数注入.属性注入.方法注入,所谓注入相当赋值,下面一个一个来介绍. 2.构造函数注入 ...

  5. Pandas 基础(13) - Crosstab 交叉列表取值

    这小节的题目看起来还挺晦涩的, crosstab 是 pandas 的一个函数, 作用还蛮强大的, 一起来看一下吧~~~ 首先还是先引入一个例子文件: import pandas as pd df = ...

  6. Linux(例如CentOS 7)打开TCP 22端口,基于SSH协议

    SSH 为 Secure Shell 的缩写,由 IETF 的网络工作小组(Network Working Group)所制定:SSH 为建立在应用层和传输层基础上的安全协议.SSH 是目前较可靠,专 ...

  7. 第 8 章 容器网络 - 072 - 一文搞懂各种 Docker 网络

    Docker 起初只提供了简单的 single-host 网络,显然这不利于 Docker 构建容器集群并通过 scale-out 方式横向扩展到多个主机上. 跨主机网络方案: Docker Over ...

  8. JVM垃圾回收(二)- Minor GC vs Major GC vs Full GC

    Minor GC vs Major GC vs Full GC 垃圾回收的活动会清理对内存中的不同区域,这些事件一般被称为Minor,Major以及Full GC events.本章我们会讨论这些清理 ...

  9. vue-cli卸载旧版,再重新安装后还显示的是旧的版本

    按vue-cli的官方文档中卸载了旧版(2.8.2),然后重装了新版本,之后输入 vue --version还是显示2.8.2,最后发现关闭终端后重新打开终端,再看vue --version显示的是新 ...

  10. linux日常使用指令总结

    linux 日常指令总结(ubuntu): ls -lha 所有文件及权限 df -h 系统磁盘空间 du -sh 当前目录大小 du -ah --max-depth=1 显示目录下所有的文件和文件夹 ...