Skip to content

Annotation scanning in enclosing class hierarchy results in NoClassDefFound  #24136

@onobc

Description

@onobc

Version: 2.2.[0|1].RELEASE

Context:

  • Custom starter library has webflux and webmvc at "provided" scope
  • Custom starter has auto-configuration that has a conditional nested configuration on each tech
  • Consuming webmvc based application includes starter (has no webflux dep)

Behavior in 2.1.x.RELEASE was that the webflux gated config would be excluded and all was well.

Behavior w/ 2.2.x.RELEASE is startup fails w/ following:

java.lang.NoClassDefFoundError: org/springframework/web/reactive/config/WebFluxConfigurer
	at java.lang.ClassLoader.defineClass1(Native Method) ~[na:1.8.0_102]
	at java.lang.ClassLoader.defineClass(ClassLoader.java:763) ~[na:1.8.0_102]
	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) ~[na:1.8.0_102]
	at java.net.URLClassLoader.defineClass(URLClassLoader.java:467) ~[na:1.8.0_102]
	at java.net.URLClassLoader.access$100(URLClassLoader.java:73) ~[na:1.8.0_102]
	at java.net.URLClassLoader$1.run(URLClassLoader.java:368) ~[na:1.8.0_102]
	at java.net.URLClassLoader$1.run(URLClassLoader.java:362) ~[na:1.8.0_102]
	at java.security.AccessController.doPrivileged(Native Method) ~[na:1.8.0_102]
	at java.net.URLClassLoader.findClass(URLClassLoader.java:361) ~[na:1.8.0_102]
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[na:1.8.0_102]
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331) ~[na:1.8.0_102]
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[na:1.8.0_102]
	at java.lang.Class.getDeclaringClass0(Native Method) ~[na:1.8.0_102]
	at java.lang.Class.getDeclaringClass(Class.java:1235) ~[na:1.8.0_102]
	at java.lang.Class.getEnclosingClass(Class.java:1277) ~[na:1.8.0_102]
	at org.springframework.core.annotation.AnnotationsScanner.processClassHierarchy(AnnotationsScanner.java:233) ~[spring-core-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.core.annotation.AnnotationsScanner.processClassHierarchy(AnnotationsScanner.java:194) ~[spring-core-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.core.annotation.AnnotationsScanner.processClass(AnnotationsScanner.java:130) ~[spring-core-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.core.annotation.AnnotationsScanner.process(AnnotationsScanner.java:107) ~[spring-core-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.core.annotation.AnnotationsScanner.scan(AnnotationsScanner.java:97) ~[spring-core-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.core.annotation.AnnotationsScanner.scan(AnnotationsScanner.java:78) ~[spring-core-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.core.annotation.TypeMappedAnnotations.scan(TypeMappedAnnotations.java:242) ~[spring-core-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.core.annotation.TypeMappedAnnotations.isPresent(TypeMappedAnnotations.java:98) ~[spring-core-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.boot.context.properties.ConfigurationPropertiesBindConstructorProvider.isConstructorBindingAnnotatedType(ConfigurationPropertiesBindConstructorProvider.java:81) ~[spring-boot-2.2.1.RELEASE.jar:2.2.1.RELEASE]
	at org.springframework.boot.context.properties.ConfigurationPropertiesBindConstructorProvider.getBindConstructor(ConfigurationPropertiesBindConstructorProvider.java:49) ~[spring-boot-2.2.1.RELEASE.jar:2.2.1.RELEASE]
	at org.springframework.boot.context.properties.ConfigurationPropertiesBean$BindMethod.forType(ConfigurationPropertiesBean.java:311) ~[spring-boot-2.2.1.RELEASE.jar:2.2.1.RELEASE]
	at org.springframework.boot.context.properties.ConfigurationPropertiesBeanDefinitionValidator.validate(ConfigurationPropertiesBeanDefinitionValidator.java:61) ~[spring-boot-2.2.1.RELEASE.jar:2.2.1.RELEASE]
	at org.springframework.boot.context.properties.ConfigurationPropertiesBeanDefinitionValidator.postProcessBeanFactory(ConfigurationPropertiesBeanDefinitionValidator.java:44) ~[spring-boot-2.2.1.RELEASE.jar:2.2.1.RELEASE]
	at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:286) ~[spring-context-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:174) ~[spring-context-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:706) ~[spring-context-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:532) ~[spring-context-5.2.1.RELEASE.jar:5.2.1.RELEASE]
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:141) ~[spring-boot-2.2.1.RELEASE.jar:2.2.1.RELEASE]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:747) [spring-boot-2.2.1.RELEASE.jar:2.2.1.RELEASE]
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) [spring-boot-2.2.1.RELEASE.jar:2.2.1.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) [spring-boot-2.2.1.RELEASE.jar:2.2.1.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226) [spring-boot-2.2.1.RELEASE.jar:2.2.1.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215) [spring-boot-2.2.1.RELEASE.jar:2.2.1.RELEASE]
	at com.example.demo.DemoApplication.main(DemoApplication.java:10) [classes/:na]
Caused by: java.lang.ClassNotFoundException: org.springframework.web.reactive.config.WebFluxConfigurer
	at java.net.URLClassLoader.findClass(URLClassLoader.java:381) ~[na:1.8.0_102]
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[na:1.8.0_102]
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331) ~[na:1.8.0_102]
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[na:1.8.0_102]
	... 39 common frames omitted

Starter snippet pom.xml

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
	<scope>provided</scope>
</dependency>

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-webflux</artifactId>
	<scope>provided</scope>
</dependency>

Starter auto-config

@Configuration
@ConditionalOnWebApplication
@EnableConfigurationProperties(DemoProperties.class)
public class DemoAutoConfiguration {

    @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
    @ConditionalOnClass(WebMvcConfigurer.class)
    @Configuration
    static class DemoWebMvcConfiguration implements WebMvcConfigurer {

        @PostConstruct
        public void init() {
            System.out.println("DemoWebMvcConfiguration.init()");
        }
    }

    @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
    @ConditionalOnClass(WebFluxConfigurer.class)
    @Configuration
    static class DemoWebFluxConfiguration implements WebFluxConfigurer {

        @PostConstruct
        public void init() {
            System.out.println("DemoWebFluxConfiguration.init()");
        }
    }
}

Consuming app pom.xml snippet

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
	<groupId>com.example</groupId>
	<artifactId>demolib</artifactId>
	<version>0.0.1-SNAPSHOT</version>
</dependency>		

NOTE: If I comment out the DemoWebMvcConfiguration the conditional behaves and the webflux gated config is excluded and all is well. It seems that when they are both present the config properties annotation scan tries to load the classes.

Metadata

Metadata

Assignees

Labels

in: coreIssues in core modules (aop, beans, core, context, expression)type: bugA general bug

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions