0%

【踩坑】引入Sentinel后项目启动报 java.lang.NoSuchMethodError org.springframework.util.Assert.notNul

引入了Sentinel的包后项目无法启动,报java.lang.NoSuchMethodError: org.springframework.util.Assert.notNull(Ljava/lang/Object;Ljava/util/function/Supplier;)V 异常,记录一下排查过程。

异常栈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
[2019-12-26 16:32:16  localhost-startStop-1 6900 ERROR o.s.w.c.ContextLoader] -- Context initialization failed
java.lang.NoSuchMethodError: org.springframework.util.Assert.notNull(Ljava/lang/Object;Ljava/util/function/Supplier;)V
at org.springframework.boot.context.properties.EnableConfigurationPropertiesImportSelector$ConfigurationPropertiesBeanRegistrar.assertHasAnnotation(EnableConfigurationPropertiesImportSelector.java:126) ~[spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE]
at org.springframework.boot.context.properties.EnableConfigurationPropertiesImportSelector$ConfigurationPropertiesBeanRegistrar.registerBeanDefinition(EnableConfigurationPropertiesImportSelector.java:119) ~[spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE]
at org.springframework.boot.context.properties.EnableConfigurationPropertiesImportSelector$ConfigurationPropertiesBeanRegistrar.register(EnableConfigurationPropertiesImportSelector.java:92) ~[spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE]
at org.springframework.boot.context.properties.EnableConfigurationPropertiesImportSelector$ConfigurationPropertiesBeanRegistrar.lambda$registerBeanDefinitions$0(EnableConfigurationPropertiesImportSelector.java:70) ~[spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE]
at org.springframework.boot.context.properties.EnableConfigurationPropertiesImportSelector$ConfigurationPropertiesBeanRegistrar$$Lambda$9/854786619.accept(Unknown Source) ~[na:na]
at java.util.ArrayList.forEach(ArrayList.java:1249) ~[na:1.8.0_51]
at org.springframework.boot.context.properties.EnableConfigurationPropertiesImportSelector$ConfigurationPropertiesBeanRegistrar.registerBeanDefinitions(EnableConfigurationPropertiesImportSelector.java:70) ~[spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE]
at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsFromRegistrars(ConfigurationClassBeanDefinitionReader.java:352) ~[spring-context-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClassBeanDefinitionReader.java:143) ~[spring-context-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClassBeanDefinitionReader.java:116) ~[spring-context-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:333) ~[spring-context-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:243) ~[spring-context-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:273) ~[spring-context-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:98) ~[spring-context-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:677) ~[spring-context-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:519) ~[spring-context-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:446) ~[spring-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:328) ~[spring-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:107) [spring-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4939) [catalina.jar:7.0.47]
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5434) [catalina.jar:7.0.47]
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150) [catalina.jar:7.0.47]
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901) [catalina.jar:7.0.47]
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877) [catalina.jar:7.0.47]
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:633) [catalina.jar:7.0.47]
at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java:1120) [catalina.jar:7.0.47]
at org.apache.catalina.startup.HostConfig$DeployDirectory.run(HostConfig.java:1678) [catalina.jar:7.0.47]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [na:1.8.0_51]
at java.util.concurrent.FutureTask.run(FutureTask.java:266) [na:1.8.0_51]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_51]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_51]
at java.lang.Thread.run(Thread.java:745) [na:1.8.0_51]

问题分析

系统中依赖了 Spring 4.2.3
Spring相关依赖

在该版本的 Spring 中只有
org.springframework.util.Assert#notNull(java.lang.Object, java.lang.String)
org.springframework.util.Assert#notNull(java.lang.Object) 这两个方法。
Sentinel 引入的 Spring-cloud 底层使用了 Spring 5.x,有 Assert.notNull(java.lang.Object, java.util.function.Supplier) 这个方法。

在系统的初始化时,扫描了底包导致 Sentinel 的这个类也被注入了

1
2
3
<context:component-scan base-package="com.xxx">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>

而扫描org.springframework.cloud.alibaba.sentinel.SentinelWebAutoConfiguration@EnableConfigurationProperties 这个注解就会调用那个不存在的 Assert.notNull 方法,源码如下图所示:
罪恶的注解

那么在配置中配置排除这个扫描这个类 Spring 就只可以正常启动

more 复习exclude-filter

在配置文件中(applicationContext.xml),

  1. 不注入指定类型的注解
1
2
3
4
5
<context:component-scan base-package="exampleBean">
<!-- 不注入 Controller 相关注解,由另外的配置扫描指定包下的 Controller -->
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
</context:component-scan>
  1. 按正则过滤指定类
1
2
3
4
5
<context:component-scan base-package="exampleBean">
<!-- 过滤掉exampleBean下的且类名以Default开头的类 -->
<context:exclude-filter type="regex"
expression="exampleBean\.Default*" />
</context:component-scan>
  1. 按指定类名过滤指定类
1
2
3
4
<context:component-scan base-package="exampleBean">
<!-- 过滤掉exampleBean下的类名为MovieFinder的类 -->
<context:exclude-filter type="assignable" expression="exampleBean.MovieFinder"/>
</context:component-scan>