问题
今天启动业余学习项目里的某服务A
发现启动失败,报错信息如下:
[ ERROR] [2025-01-05 15:41:26,083] [main] com.cdfive.springboot.startup.ApplicationStartupExceptionReporter [30] - error=>
java.lang.ArrayStoreException: sun.reflect.annotation.TypeNotPresentExceptionProxyat sun.reflect.annotation.AnnotationParser.parseClassArray(AnnotationParser.java:724)at sun.reflect.annotation.AnnotationParser.parseArray(AnnotationParser.java:531)at sun.reflect.annotation.AnnotationParser.parseMemberValue(AnnotationParser.java:355)at sun.reflect.annotation.AnnotationParser.parseAnnotation2(AnnotationParser.java:286)at sun.reflect.annotation.AnnotationParser.parseAnnotations2(AnnotationParser.java:120)at sun.reflect.annotation.AnnotationParser.parseAnnotations(AnnotationParser.java:72)at java.lang.reflect.Executable.declaredAnnotations(Executable.java:599)at java.lang.reflect.Executable.declaredAnnotations(Executable.java:597)at java.lang.reflect.Executable.getDeclaredAnnotations(Executable.java:588)at java.lang.reflect.Method.getDeclaredAnnotations(Method.java:630)at org.springframework.core.annotation.AnnotationsScanner.getDeclaredAnnotations(AnnotationsScanner.java:454)at org.springframework.core.annotation.AnnotationsScanner.isKnownEmpty(AnnotationsScanner.java:492)at org.springframework.core.annotation.TypeMappedAnnotations.from(TypeMappedAnnotations.java:251)at org.springframework.core.annotation.MergedAnnotations.from(MergedAnnotations.java:351)at org.springframework.core.annotation.MergedAnnotations.from(MergedAnnotations.java:330)at org.springframework.core.annotation.AnnotatedElementUtils.findAnnotations(AnnotatedElementUtils.java:764)at org.springframework.core.annotation.AnnotatedElementUtils.hasAnnotation(AnnotatedElementUtils.java:531)at org.springframework.context.annotation.BeanAnnotationHelper.isBeanAnnotated(BeanAnnotationHelper.java:41)at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.isMatch(ConfigurationClassEnhancer.java:407)at org.springframework.context.annotation.ConfigurationClassEnhancer$ConditionalCallbackFilter.accept(ConfigurationClassEnhancer.java:192)at org.springframework.cglib.proxy.Enhancer.emitMethods(Enhancer.java:1217)at org.springframework.cglib.proxy.Enhancer.generateClass(Enhancer.java:726)at org.springframework.cglib.transform.TransformingClassGenerator.generateClass(TransformingClassGenerator.java:33)at org.springframework.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)at org.springframework.cglib.core.ClassLoaderAwareGeneratorStrategy.generate(ClassLoaderAwareGeneratorStrategy.java:57)at org.springframework.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:358)at org.springframework.cglib.proxy.Enhancer.generate(Enhancer.java:585)at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:110)at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:108)at org.springframework.cglib.core.internal.LoadingCache$2.call(LoadingCache.java:54)at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266)at java.util.concurrent.FutureTask.run(FutureTask.java)at org.springframework.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:61)at org.springframework.cglib.core.internal.LoadingCache.get(LoadingCache.java:34)at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:134)at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:319)at org.springframework.cglib.proxy.Enhancer.createHelper(Enhancer.java:572)at org.springframework.cglib.proxy.Enhancer.createClass(Enhancer.java:419)at org.springframework.context.annotation.ConfigurationClassEnhancer.createClass(ConfigurationClassEnhancer.java:137)at org.springframework.context.annotation.ConfigurationClassEnhancer.enhance(ConfigurationClassEnhancer.java:109)at org.springframework.context.annotation.ConfigurationClassPostProcessor.enhanceConfigurationClasses(ConfigurationClassPostProcessor.java:433)at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanFactory(ConfigurationClassPostProcessor.java:258)at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:291)at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:131)at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:707)at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:533)at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:143)at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:755)at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:747)at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:402)at org.springframework.boot.SpringApplication.run(SpringApplication.java:312)at org.springframework.boot.SpringApplication.run(SpringApplication.java:1247)at org.springframework.boot.SpringApplication.run(SpringApplication.java:1236)at com.cdfive.web.WebApplication.main(WebApplication.java:16)
第一次看到这个报错:java.lang.ArrayStoreException: sun.reflect.annotation.TypeNotPresentExceptionProxy
异常堆栈信息里没有项目模块里相关的东西,初步看不出来哪里的问题。
分析&解决
考虑上周在本机尝试安装新版本IDEA,后面又还原安装了老版本,尝试刷新整个项目Maven依赖,然后mvn clean package
重新启动该模块,发现还是启动失败;
尝试启动项目里另一个模块服务B
,启动成功。
咦?这什么情况。。
查看Git Log想想自己怎么改了什么,看到最近对对cdfive-sentinel
、cdfive-demo-mybatis
模块有修改和优化,
cdfive-sentinel
模块里增加了feign
的集成扩展,通过feign
的Capability
和InvocationHandlerFactory
和sentinel
的API进行集成,
cdfive-demo-mybatis模块
里引入knife4j-spring-ui
依赖,修复了影响其使用的问题。
其中,cdfive-demo-mybatis
是个独立的demo模块,服务A
和服务B
依赖它,而都有对cdfive-sentinel
模块的依赖。
为什么服务A
启动失败,服务B
启动成功呢?
注意到服务A
没有feign
的依赖,而服务B
有,在cdfive-sentinel
模块里对feign
的依赖<scope>
为provided
。
通过搜索找到一些博客分享,尝试在java.lang.ArrayStoreException
类的构造函数打断点:
public ArrayStoreException() {super();}public ArrayStoreException(String s) {super(s);
}
在断点的方法调用栈里,看到上一级AnnotationParser#parseClassArray(...)
里,找到了有个变量的类型是com.cdfive.sentinel.config.SentinelAutoConfiguration
,
是扩展sentinel
的自动配置类,上周正好修改过。
打开SentinelAutoConfiguration
类,查看新添加的代码:
@ConditionalOnClass(Feign.Builder.class)
@Bean
public SentinelBeanPostProcessor sentinelBeanPostProcessor() {SentinelBeanPostProcessor sentinelBeanPostProcessor = new SentinelBeanPostProcessor();return sentinelBeanPostProcessor;
}
新添加1个SentinelBeanPostProcessor
的Bean,想通过@ConditionalOnClass
注解实现当class包里有Feign.Builder
时创建该Bean,
而服务A
里没有feign
的依赖包。
根据@ConditionalOnClass
注解字面理解,它是判断class是否存在来决定是否创建Bean,
这里将注解用在方法上,遇到了问题。
查看@ConditionOnClass
注解的注释:
*** {@link Conditional @Conditional} that only matches when the specified classes are on* the classpath.* <p>* A {@link #value()} can be safely specified on {@code @Configuration} classes as the* annotation metadata is parsed by using ASM before the class is loaded. Extra care is* required when placed on {@code @Bean} methods, consider isolating the condition in a* separate {@code Configuration} class, in particular if the return type of the method* matches the {@link #value target of the condition}.** @author Phillip Webb* @since 1.0.0*/
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass {...
}
根据查询的资料:
`@ConditionalOnClass`如果添加在类的方法里时要格外小心,考虑将条件隔离在单独的Configuration类中,
建议`@ConditionalOnClass`在类级别使用,如果在方法上使用,考虑使用它的name属性来指定。
因此尝试将@ConditionalOnClass(Feign.Builder.class)
修改为@ConditionalOnClass(name = "feign.Feign.Builder")
,
通过名称而不是类型来指定,重新编译项目并启动服务A
,启动成功。
参考
-
springboot启动报sun.reflect.annotation.TypeNotPresentExceptionProxy
https://zhuanlan.zhihu.com/p/462068881 -
ConditionalOnClass not working for Bean methods on Java 8
https://github.com/spring-projects/spring-boot/issues/27846