You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot报错:无法继承final类,标记open后依赖注入为空

问题描述

  • 后端基于Spring Boot + Kotlin开发,数月后部分控制器突然报错:Error creating bean with name 'authApi' defined in file ... Cannot subclass final class
  • 手动给类添加open修饰符后,构造方法注入的参数、初始化属性全部为空
  • 问题触发场景:更新两个子模块的Gradle配置并禁用bootJar任务之后
  • 调试发现:原对象的属性值正常,但接口调用时访问的是代理对象的属性(未被注入)

示例控制器代码:

@RestController
@RequestMapping("/api")
open class AuthApi(
    private val userManager: UserManager,
    private val authenticationManager: AuthenticationManager,
    private val jwtEncoder: JwtEncoder
){
    val logger = LoggerFactory.getLogger(AuthApi::class.java)
}

核心原因

  1. Kotlin Spring插件未生效:kotlin("plugin.spring")插件会自动将Spring组件(@RestController、@Service等)标记为open,同时处理构造注入的代理逻辑。但子模块Gradle配置中未应用该插件,手动加open后代理逻辑失效,导致属性无法注入到代理对象。
  2. 多模块Gradle配置冲突:主项目在allprojects中全局应用org.springframework.boot插件,而子模块作为库模块禁用bootJar后,插件的部分自动配置未正常运行。

修复步骤

1. 修正子模块Gradle配置

给两个子模块添加kotlin("plugin.spring")插件,让Spring自动处理类的open修饰和代理逻辑,无需手动给类加open。

以模块1为例,修改build.gradle.kts:

import org.springframework.boot.gradle.tasks.bundling.BootJar

plugins {
    id("org.springframework.boot") apply false
    id("io.spring.dependency-management")
    kotlin("jvm")
    kotlin("plugin.spring") // 新增该插件
}

tasks.getByName<BootJar>("bootJar") {
    enabled = false
}

tasks.getByName<Jar>("jar") {
    enabled = true
}

dependencies {
    // 原依赖保持不变
    implementation("org.springframework.boot:spring-boot-starter-data-rest:2.7.8")
    implementation("org.springframework.boot:spring-boot-starter-security:2.7.8")
    // ... 其他依赖
}

2. 移除手动添加的open关键字

将控制器类的open修饰符删除,交由插件自动处理:

@RestController
@RequestMapping("/api")
class AuthApi(
    private val userManager: UserManager,
    private val authenticationManager: AuthenticationManager,
    private val jwtEncoder: JwtEncoder
){
    val logger = LoggerFactory.getLogger(AuthApi::class.java)
}

3. 调整主项目Gradle配置

避免在allprojects中全局应用org.springframework.boot插件,仅在主项目生效,同时给子模块统一添加Spring插件:

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import com.github.gradle.node.yarn.task.YarnTask

buildscript{
    repositories {
        maven {
            url = uri("https://plugins.gradle.org/m2/")
        }
    }
    dependencies{
        classpath("com.github.node-gradle:gradle-node-plugin:3.2.1")
    }
}

plugins {
    id("org.springframework.boot") version "2.7.8"
    id("io.spring.dependency-management") version "1.1.0"
    kotlin("jvm") version "1.6.10"
    kotlin("plugin.spring") version "1.6.10"
    kotlin("plugin.jpa") version "1.5.21"
    id("com.github.node-gradle.node") version "3.2.1"
}

allprojects {
    apply(plugin = "io.spring.dependency-management")
    apply(plugin = "com.github.node-gradle.node")
    apply(plugin = "java")
    apply(plugin = "kotlin")
    group = "com.test"
    version = "0.9.0"
    java.sourceCompatibility = JavaVersion.VERSION_17
    repositories {
        mavenCentral()
    }
}

subprojects {
    apply(plugin = "java")
    apply(plugin = "kotlin")
    apply(plugin="io.spring.dependency-management")
    apply(plugin="kotlin.plugin.spring") // 子模块统一应用Spring插件
}

tasks.withType<KotlinCompile> {
    kotlinOptions {
        freeCompilerArgs = listOf("-Xjsr305=strict")
        jvmTarget = "17"
    }
}

tasks.withType<Test> {
    useJUnitPlatform()
}

dependencies {
    // 原依赖保持不变
    implementation(project(":authentication"))
    implementation(project(":commerce"))
    // ... 其他依赖
}

4. 清理并重新构建

执行Gradle命令清理缓存并重新构建:

./gradlew clean build

原理说明

kotlin("plugin.spring")插件会自动完成两个关键操作:

  • 识别带有Spring注解的类,自动标记为open,允许Spring创建子类代理
  • 处理构造注入的代理逻辑,确保代理对象能正确引用原对象的属性和方法

之前手动添加open但缺少插件支持时,Spring创建的代理对象无法正确传递构造参数到原对象,导致属性为空;子模块未应用插件则是因为主项目的全局配置与子模块的库模式冲突,导致自动配置失效。

内容的提问来源于stack exchange,提问作者Krisztian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 11:31:01