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

如何让Java11的javax.validation通用库适配Java17 Spring Boot3服务?

问题:Spring 3.2(Java 17)无法兼容common-lib中的javax.validation自定义校验注解

项目环境

  • common-lib:Java 11开发,无Spring依赖,包含共享类、错误码、模型、DTO及自定义校验注解
  • x服务:Java 11 + Spring 2.6,可正常使用common-lib所有功能
  • y服务:Java 17 + Spring 3.2,仅能使用common-lib中的模型、错误码等类,无法识别其中的自定义校验注解

问题核心

y服务基于Spring 3.2,依赖jakarta.validation体系,而common-lib使用的是旧版javax.validation注解。由于Java EE迁移到Jakarta EE时包名从javax改为jakarta,导致字节层面不兼容,y服务无法解析common-lib中的自定义校验注解。

自定义校验注解代码

@Target({TYPE})
@Retention(RUNTIME)
@Constraint(validatedBy = IsValidSms.SmsValidator.class)
public @interface IsValidSms {

    String message() default "INVALID_REQUEST_BODY";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    class SmsValidator implements ConstraintValidator<IsValidSms, SmsRequest> {

        public boolean isValid(SmsRequest smsRequest, ConstraintValidatorContext constraintContext) {
            final String smsType = smsRequest.getSmsType();
            if (Objects.isNull(smsType) || smsType.isBlank()) {
                return Objects.nonNull(smsRequest.getContent());
            }
            return isValidPlaceholders(smsRequest.getPlaceholders());
        }

        private boolean isValidPlaceholders(Map<String, String> placeholders) {
            if (Objects.isNull(placeholders) || placeholders.isEmpty()) {
                return true;
            }
            return placeholders.keySet().stream().allMatch(key -> key.startsWith("{{") && key.endsWith("}}"));
        }
    }
}

common-lib依赖配置

dependencies {
    testImplementation 'org.mockito:mockito-core:3.5.9'
    testImplementation 'org.mockito:mockito-junit-jupiter:3.5.9'
    compileOnly 'org.projectlombok:lombok'
    implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.11.3'
    implementation group: 'org.hibernate.validator', name: 'hibernate-validator', version: '6.1.6.Final'
    implementation 'commons-validator:commons-validator:1.7'
    testImplementation group: 'org.glassfish', name: 'javax.el', version: '3.0.0'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.5.2'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.5.2'
    testRuntimeOnly 'org.junit.vintage:junit-vintage-engine:5.5.2'
    implementation 'org.springframework:spring-beans:5.3.20'
}

已尝试无效方案

  • 给y服务添加jakarta.validation依赖
  • 给y服务手动添加javax.validation依赖

解决方案(无需修改common-lib)

方案1:Spring原生桥接适配

直接在y服务中添加Spring提供的校验适配依赖,自动兼容javax.validation注解:

// Gradle配置
implementation 'org.springframework.boot:spring-boot-starter-validation'
// 引入javax.validation API用于兼容旧注解
implementation 'javax.validation:validation-api:2.0.1.Final'
// 使用兼容jakarta的Hibernate Validator版本
runtimeOnly 'org.hibernate.validator:hibernate-validator:7.0.4.Final'
// 核心:Spring的validation模块负责适配javax与jakarta
implementation 'org.springframework.validation:spring-validation'

如果是Maven项目:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>2.0.1.Final</version>
</dependency>
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>7.0.4.Final</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-validation</artifactId>
</dependency>

Spring的spring-validation模块会自动识别javax.validation注解,并适配到jakarta.validation的处理流程中,无需额外配置。

方案2:字节码自动转换

使用OpenRewrite插件在构建时自动修改common-lib中的字节码,将javax.validation替换为jakarta.validation:

// Gradle配置
plugins {
    id 'org.openrewrite.rewrite' version '5.40.0'
}

rewrite {
    // 启用javax到jakarta的迁移规则
    activeRecipe('org.openrewrite.java.spring.JavaxMigrationToJakarta')
}

dependencies {
    rewrite 'org.openrewrite.recipe:rewrite-spring:5.40.0'
}

执行构建时,插件会自动扫描所有依赖包,将其中的javax.validation相关类、注解替换为jakarta.validation的对应内容,从字节层面解决兼容问题。

方案3:本地注解覆盖

在y服务中创建与common-lib完全同名同路径的自定义校验注解,但使用jakarta.validation的API:

// 包路径必须与common-lib中IsValidSms完全一致
package com.your.common.package;

import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

import java.lang.annotation.Target;
import java.lang.annotation.Retention;
import java.util.Map;
import java.util.Objects;

import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Target({TYPE})
@Retention(RUNTIME)
@Constraint(validatedBy = IsValidSms.SmsValidator.class)
public @interface IsValidSms {

    String message() default "INVALID_REQUEST_BODY";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    class SmsValidator implements ConstraintValidator<IsValidSms, SmsRequest> {

        // 完全复制common-lib中SmsValidator的校验逻辑
        public boolean isValid(SmsRequest smsRequest, ConstraintValidatorContext constraintContext) {
            final String smsType = smsRequest.getSmsType();
            if (Objects.isNull(smsType) || smsType.isBlank()) {
                return Objects.nonNull(smsRequest.getContent());
            }
            return isValidPlaceholders(smsRequest.getPlaceholders());
        }

        private boolean isValidPlaceholders(Map<String, String> placeholders) {
            if (Objects.isNull(placeholders) || placeholders.isEmpty()) {
                return true;
            }
            return placeholders.keySet().stream().allMatch(key -> key.startsWith("{{") && key.endsWith("}}"));
        }
    }
}

利用类加载的优先级,y服务会优先加载本地的IsValidSms注解,从而适配jakarta.validation体系。这种方式适合单个注解的适配,但需要保持与common-lib的校验逻辑同步。

优先级推荐

  1. 优先使用方案1,Spring原生适配,配置简单且无需额外维护
  2. 若有大量javax依赖需要迁移,选择方案2,一次性解决所有兼容问题
  3. 仅单个或少数注解兼容问题时,可使用方案3,快速临时解决

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 03:36:01