最新消息:Welcome to the puzzle paradise for programmers! Here, a well-designed puzzle awaits you. From code logic puzzles to algorithmic challenges, each level is closely centered on the programmer's expertise and skills. Whether you're a novice programmer or an experienced tech guru, you'll find your own challenges on this site. In the process of solving puzzles, you can not only exercise your thinking skills, but also deepen your understanding and application of programming knowledge. Come to start this puzzle journey full of wisdom and challenges, with many programmers to compete with each other and show your programming wisdom! Translated with DeepL.com (free version)

How can I use a common Java 11 library with validation annotations in a Java 17 Spring Boot service? - Stack Overflow

matteradmin4PV0评论

I have three projects:

1- common-lib, Written with Java 11. It doesn't use Spring, but contains shared classes, error codes, models, DTOs, etc., including custom validation annotations (see below).

2- x service, Written with Java 11, using Spring 2.6.

3- y service, Written with Java 17, using Spring 3.2.

Problem: My Java 11 services (x service) can use common-lib easily, including the validation annotations. However, my Java 17 service (y service) can use the models, error codes, and other common classes from common-lib, but cannot use the validation annotations.

The issue appears to be related to the transition from javax.validation to jakarta.validation and other potential bytecode issues.

@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("}}"));
        }
    }
}

Context: common-lib is in Java 11 and uses javax.validation annotations. y service is in Java 17 and uses Spring 3.2, which is built on jakarta.validation. The Java 17 service can’t resolve the javax.validation annotations from the common-lib due to changes in the bytecode and the switch from javax to jakarta.

Question: How can I resolve this issue and make the common-lib (Java 11, javax.validation) work with the Java 17 service (which uses Spring 3.2 and jakarta.validation)?

What I've tried: Including jakarta.validation dependencies in the y service, but that doesn't resolve the issue. Adding javax.validation dependency manually to the y service. I'm looking for a solution that allows me to use the validation annotations in the y service without changing the common-lib project.

common-lib ->

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

solution suggestion.

I have three projects:

1- common-lib, Written with Java 11. It doesn't use Spring, but contains shared classes, error codes, models, DTOs, etc., including custom validation annotations (see below).

2- x service, Written with Java 11, using Spring 2.6.

3- y service, Written with Java 17, using Spring 3.2.

Problem: My Java 11 services (x service) can use common-lib easily, including the validation annotations. However, my Java 17 service (y service) can use the models, error codes, and other common classes from common-lib, but cannot use the validation annotations.

The issue appears to be related to the transition from javax.validation to jakarta.validation and other potential bytecode issues.

@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("}}"));
        }
    }
}

Context: common-lib is in Java 11 and uses javax.validation annotations. y service is in Java 17 and uses Spring 3.2, which is built on jakarta.validation. The Java 17 service can’t resolve the javax.validation annotations from the common-lib due to changes in the bytecode and the switch from javax to jakarta.

Question: How can I resolve this issue and make the common-lib (Java 11, javax.validation) work with the Java 17 service (which uses Spring 3.2 and jakarta.validation)?

What I've tried: Including jakarta.validation dependencies in the y service, but that doesn't resolve the issue. Adding javax.validation dependency manually to the y service. I'm looking for a solution that allows me to use the validation annotations in the y service without changing the common-lib project.

common-lib ->

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

solution suggestion.

Share Improve this question edited Nov 19, 2024 at 12:44 Mert Karaman asked Nov 18, 2024 at 13:10 Mert KaramanMert Karaman 112 bronze badges 1
  • 1 You don't. You cannot mix those, that being said the annotations are just metadata. Just put both annotations (javax.validation and jakarta.validation) on your classes in common-lib and based on what is being used it will use the proper one and ignore the other. – M. Deinum Commented Nov 18, 2024 at 13:29
Add a comment  | 

1 Answer 1

Reset to default 1

You have to make two annotation types, which will be identical except for their import statements. The good news is, they can coexist without any problems:

import java.util.Map;
import java.util.Objects;

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

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

And:

import java.util.Map;
import java.util.Objects;

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

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

And of course, in your code, you’ll have things like:

@IsValidSms
@IsValidSmsLegacy
public class Message {
    // ...
}

Side note: Objects.isNull is not meant to be a universal replacement for the != null check. Objects.isNull is intended for cases where code needs a method reference; for example:

public void process(Collection<String> values) {
    Objects.requireNonNull(values, "Value set cannot be null.");
    if (values.stream().anyMatch(Objects::isNull)) {
        throw new IllegalArgumentException(
            "Value set cannot contain null elements.");
    }

    // ...
}

Articles related to this article

Post a comment

comment list (0)

  1. No comments so far