common-problems 2 min read

Fixing Spring Configuration Issues in Kotlin: The Final Class Problem

Learn how to fix the 'Classes annotated with @Configuration must not be final' error in Kotlin Spring Boot applications using kotlin-allopen plugin. Complete guide with Maven & Gradle examples.

If you're working with Spring Boot and Kotlin, you might encounter this error when creating @Configuration classes:

Classes annotated with @Configuration could be implicitly subclassed and must not be final

This article will explain why this happens and show you how to fix it properly in both Maven and Gradle projects.

Understanding the Problem

Why Does This Happen?

The issue stems from a fundamental difference between Kotlin and Java:

  1. In Kotlin, all classes are final by default (cannot be inherited)
  2. Spring Framework needs to create proxy classes by subclassing certain Spring-annotated classes
  3. This creates a conflict when Spring tries to subclass your Kotlin classes

This particularly affects classes with these annotations:

  • @Configuration
  • @Component
  • @Service
  • @Repository
  • @Controller
  • And others...

Solutions

You could manually add the open keyword to each class:

@Configuration
open class MyConfiguration {
    // Configuration code
}

However, this approach:

  • Is tedious
  • Prone to errors
  • Requires manual maintenance
  • Not scalable for larger projects

A better solution is to use the kotlin-allopen plugin, which automatically makes classes with specific annotations open.

For Maven Projects:

<plugin>
    <groupId>org.jetbrains.kotlin</groupId>
    <artifactId>kotlin-maven-plugin</artifactId>
    <version>${kotlin.version}</version>
    <configuration>
        <compilerPlugins>
            <plugin>spring</plugin>
        </compilerPlugins>
    </configuration>
    <dependencies>
        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-maven-allopen</artifactId>
            <version>${kotlin.version}</version>
        </dependency>
    </dependencies>
    <executions>
        <execution>
            <id>compile</id>
            <phase>compile</phase>
            <goals>
                <goal>compile</goal>
            </goals>
        </execution>
        <execution>
            <id>test-compile</id>
            <phase>test-compile</phase>
            <goals>
                <goal>test-compile</goal>
            </goals>
        </execution>
    </executions>
</plugin>

For Gradle Projects (build.gradle.kts):

plugins {
    id("org.jetbrains.kotlin.plugin.spring") version "2.1.0"
}

What Does the Plugin Do?

The kotlin-allopen plugin with the Spring preset automatically makes classes open when they have these annotations:

  • @Component
  • @Async
  • @Transactional
  • @Cacheable
  • @SpringBootTest

Additionally, thanks to meta-annotations, classes with these annotations are also automatically opened:

  • @Configuration
  • @Controller
  • @RestController
  • @Service
  • @Repository

Best Practices

  1. Always use the kotlin-allopen plugin in Spring Boot projects with Kotlin
  2. Keep your build configuration up to date with the latest Kotlin version
  3. Understand which classes need to be open and why
  4. Use Spring Boot's starter for Kotlin when available

Common Issues and Solutions

Issue 1: Plugin Not Working

  • Verify plugin version matches your Kotlin version
  • Ensure proper plugin configuration in build file
  • Check if annotations are imported correctly

Issue 2: Mixed Java/Kotlin Projects

  • Configure source directories correctly
  • Use consistent plugin versions
  • Consider gradual migration strategies

Conclusion

The kotlin-allopen plugin is the recommended way to handle Spring's proxy requirements in Kotlin projects. It provides an elegant solution that maintains Kotlin's safety features while working seamlessly with Spring's proxy-based features.

Further Reading