Problem description:
We started out on a new Kotlin-based Spring Boot project using Spring Modulith to enforce module boundaries.
The package structure is as follows:
my.company.myapp
-> MySpringBootApp
-> config
-> SpringSecurityConfig.kt
-> SecurityProperties.kt
-> usecase1
-> <fleshed out implementation for use case 1>
-> usecase2..usecaseN
-> no implementation as of yet
The SecurityProperties class looks like this:
@ConfigurationProperties(prefix = "security")
data class SecurityProperties(
val users: List<User>
)
data class User(
val username: String,
val password: String,
val roles: Set<Role>
)
enum class Role {
MONITORING,
API_USER
}
We also use TestContainers and a derived Spring Boot Application Class in src/test/kotlin for local deployment:
object MyLocalSpringBootApp {
@JvmStatic
fun main(args: Array<String>) {
fromApplication<MySpringBootApp>()
.with(ContainersConfig::class.java)
.run(*args)
}
}
When we start MyLocalSpringBootApp, we get the following exception:
...
Caused by: org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class my.company.myapp.config.SecurityProperties: Common causes of this problem include using a final class or a non-visible class
at org.springframework.aop.framework.CglibAopProxy.buildProxy(CglibAopProxy.java:227)
at org.springframework.aop.framework.CglibAopProxy.getProxy(CglibAopProxy.java:160)
at org.springframework.aop.framework.ProxyFactory.getProxy(ProxyFactory.java:110)
at org.springframework.modulith.observability.ModuleTracingSupport.addAdvisor(ModuleTracingSupport.java:58)
at org.springframework.modulith.observability.ModuleTracingSupport.addAdvisor(ModuleTracingSupport.java:42)
at org.springframework.modulith.observability.ModuleTracingBeanPostProcessor.lambda$postProcessAfterInitialization$0(ModuleTracingBeanPostProcessor.java:92)
at java.base/java.util.Optional.map(Optional.java:260)
at org.springframework.modulith.observability.ModuleTracingBeanPostProcessor.postProcessAfterInitialization(ModuleTracingBeanPostProcessor.java:87)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:437)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1776)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:599)
... 51 common frames omitted
We use kotlin-maven-plugin with "spring" plugin, so the fact that we use an immutable Kotlin data class with 'val' shouldn't be a problem.
Workarounds:
When I remove all Spring Modulith dependencies from pom.xml, MyLocalSpringBootApp starts up successfully.
Also, if I add "@Configuration" to SecurityProperties (which is redundant) and keep the Spring Modulith dependencies in pom.xml, the app also starts up successfully.
The app also starts successfully if we move the classes in "config" to "usecase1", though I suspect this will break once we implement the other use cases.
Questions:
Any idea what's going on here? Is this a misconfiguration / misuse on our side? A Spring Modulith bug?
Any help is highly appreciated!
More details:
Spring Boot 3.2.1
Spring Modulith 1.1.2
Kotlin 1.9.21
kotlin-maven-plugin configuration:
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<sourceDirs>
<sourceDir>${project.basedir}/src/main/kotlin</sourceDir>
</sourceDirs>
</configuration>
</execution>
<execution>
<id>test-compile</id>
<goals>
<goal>test-compile</goal>
</goals>
<configuration>
<sourceDirs>
<sourceDir>${project.basedir}/src/test/kotlin</sourceDir>
</sourceDirs>
</configuration>
</execution>
</executions>
<configuration>
<args>
<arg>-Xjsr305=strict</arg>
<arg>-opt-in=kotlin.RequiresOptIn</arg>
<arg>-java-parameters</arg>
</args>
<compilerPlugins>
<plugin>spring</plugin>
<plugin>jpa</plugin>
</compilerPlugins>
</configuration>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-allopen</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-noarg</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
</plugin>