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. 51nod1268 和为K的组合(DFS)

    1268 和为K的组合  基准时间限制:1 秒 空间限制:131072 KB 分值: 20 难度:3级算法题  收藏  关注 给出N个正整数组成的数组A,求能否从中选出若干个,使他们的和为K.如果可以 ...

  2. 2、每日复习点--ConcurrentHashMap vs HashMap vs HashTable

    HashMap:允许键或者值为null.底层是基于数组+链表的结构.默认初始容量是16,默认加载因子是0.75F,默认扩容增加一倍.如果指定了初始容量n,n必然在(2x,2x+1]范围内,n经过运算所 ...

  3. 泛型集合List的详细用法

    命名空间:   System.Collections.Generic List<T>类是 ArrayList 类的泛型等效类.    该类使用大小可 按需动态增加 的数组实现 IList& ...

  4. FIT2096 Assignment 2 2019

    FIT2096 Assignment 2 2019_v1.2.docx 9/04/2019 11:34 AMPage 1 | 4FIT2096 Assignment 2 2019Transformat ...

  5. java开发工具STS的下载及安装

    将下载后的压缩文件解压,在解压后的sts-bundle下的sts-3.9.1RELEASE目录中STS.exe便是可执行程序,用于启动STS,将该文件发送到桌面快捷方式,当我们想使用STS时可以快速的 ...

  6. cv2.getRotationMatrix2D函数

  7. 【新特性】JDK1.6

    一.Desktop类和SystemTray类 在JDK6中 ,AWT新增加了两个类:Desktop和SystemTray. 前者可以用来打开系统默认浏览器浏览指定的URL,打开系统默认邮件客户端给指定 ...

  8. Win10下JDK下载与环境变量配置

    一.JDK下载 1.JDK下载地址:https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.ht ...

  9. WebPack命令执行的时候,其内部处理逻辑是什么

    1.首先webpack发现并没有通过命令的形式,给它指定入口和出口 2.向根目录查找“webpack.config.js”配置文件 3.解析这个配置文件,并得到配置文件中导出的配置对象 4.当拿到配置 ...

  10. 解决ubuntu无法使用root用户启动Google Chrome浏览器

    1.找到Chrome的路径 # whereis google-chrome google-chrome: /usr/bin/google-chrome /usr/share/man/man1/goog ...