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

Spring Boot项目Bean Validation验证失效问题求助

Spring Boot Bean Validation 验证规则完全失效问题

我在Spring Boot项目中使用Bean Validation时遇到问题,已尝试更换项目依赖、重建项目(怀疑项目损坏)、在其他机器运行项目,但仍出现相同错误——DTO中的验证规则完全不起作用。以下是相关代码信息:

POM文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.0.7</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>sustenapp</groupId>
    <artifactId>sustenApp</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>sustenApp_API</name>
    <description>sustenApp</description>

    <properties>
        <java.version>17</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.flywaydb</groupId>
            <artifactId>flyway-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <dependency>
            <groupId>br.com.caelum.stella</groupId>
            <artifactId>caelum-stella-core</artifactId>
            <version>2.1.2</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>4.4.0</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.5.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.5.1</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
            <version>2.0.0</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

实体类 EnderecoModel

import java.io.Serial;
import java.io.Serializable;
import java.util.UUID;

@Data
@AllArgsConstructor
@NoArgsConstructor
@DynamicUpdate
@Builder
@Entity
@Table(name = "endereco")
public class EnderecoModel implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;

    @Id @GeneratedValue(strategy = GenerationType.AUTO)
    private UUID id;

    private String logradouro, complemento, cep, cidade, estado;
    private UUID usuario;
}

DTO类 EnderecoDto

import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Builder;
import lombok.Data;
import sustenapp_api.component.constraint.annotation.EnderecoVerify;
import sustenapp_api.component.constraint.annotation.UsuarioVerify;

import java.util.UUID;

@Data
@Builder
@EnderecoVerify
public class EnderecoDto {
    @NotNull @NotEmpty
    private String logradouro, complemento, cep, cidade, estado;

    @NotNull @UsuarioVerify
    private UUID usuario;
}

测试代码

@Test
@DisplayName("Testes de Cobertura e Validação do Metodo Save")
public void save(){
    var atual = service.save(factoryDto(usuario));
    var esperado = factoryDto(usuario);

    assertAll(
            () -> assertEquals(esperado.getUsuario(), atual.getUsuario()),
            () -> assertEquals(esperado.getCep(), atual.getCep()),
            () -> assertEquals(esperado.getCidade(), atual.getCidade()),
            () -> assertEquals(esperado.getEstado(), atual.getEstado()),
            () -> assertEquals(esperado.getLogradouro(), atual.getLogradouro()),
            () -> assertEquals(esperado.getComplemento(), atual.getComplemento()),

            () -> assertThrows(
                    // @NotNull - OBSERVACAO
                    Exception.class, () -> service.save(EnderecoDto.builder().build())
            ),
            () -> assertThrows(
                    // @NotEmpty - OBSERVACAO
                    Exception.class, () -> service.save(EnderecoUtil.factoryDto(UUID.fromString(USUARIO), LOGRADOURO, COMPLEMENTO, "", CIDADE, ESTADO))
            ),
            () -> assertThrows(
                    // @NotEmpty - OBSERVACAO
                    Exception.class, () -> service.save(EnderecoUtil.factoryDto(UUID.fromString(USUARIO),"", COMPLEMENTO, CEP, CIDADE, ESTADO))
            ),
            () -> assertThrows(
                    // @NotEmpty - OBSERVACAO
                    Exception.class, () -> service.save(EnderecoUtil.factoryDto(UUID.fromString(USUARIO), LOGRADOURO, "", CEP, CIDADE, ESTADO))
            ),
            () -> assertThrows(
                    // @NotEmpty - OBSERVACAO
                    Exception.class, () -> service.save(EnderecoUtil.factoryDto(UUID.fromString(USUARIO), LOGRADOURO, COMPLEMENTO, CEP, "", ESTADO))
            ),
            () -> assertThrows(
                    // @NotEmpty - OBSERVACAO
                    Exception.class, () -> service.save(EnderecoUtil.factoryDto(UUID.fromString(USUARIO), LOGRADOURO, COMPLEMENTO, CEP, CIDADE, ""))
            ),
            () -> assertThrows(
                    // @UserVerify - OBSERVACAO
                    Exception.class, () -> service.save(EnderecoUtil.factoryDto(UUID.fromString(USUARIO), LOGRADOURO, COMPLEMENTO, CEP, CIDADE, ESTADO))
            )
        );
}

问题排查与解决方案

1. 验证触发点缺失

Bean Validation不会自动执行,必须显式触发:

  • 控制器层:在接收DTO的参数上添加@Valid或@Validated注解:
    @PostMapping("/enderecos")
    public ResponseEntity<?> createEndereco(@Valid @RequestBody EnderecoDto dto) {
        // 业务逻辑
    }
    
  • 服务层:注入Validator手动触发验证:
    @Service
    public class EnderecoService {
        private final Validator validator;
    
        public EnderecoService(Validator validator) {
            this.validator = validator;
        }
    
        public EnderecoDto save(EnderecoDto dto) {
            Set<ConstraintViolation<EnderecoDto>> violations = validator.validate(dto);
            if (!violations.isEmpty()) {
                throw new ConstraintViolationException(violations);
            }
            // 后续保存逻辑
        }
    }
    

2. 自定义注解实现检查

确认@EnderecoVerify和@UsuarioVerify的实现:

  • 注解需标注@Constraint(validatedBy = 对应验证器.class)
  • 验证器类必须实现ConstraintValidator<自定义注解, 目标类型>接口,且正确实现isValid方法

3. 测试类中补充验证逻辑

当前测试直接调用service.save(),若服务层未触发验证,测试不会抛出预期异常。可在测试中手动验证:

@Test
public void testInvalidDto() {
    EnderecoDto invalidDto = EnderecoDto.builder().build();
    Set<ConstraintViolation<EnderecoDto>> violations = validator.validate(invalidDto);
    assertFalse(violations.isEmpty());
}

4. 依赖兼容性确认

Spring Boot 3.x 使用Jakarta EE规范,你已正确使用jakarta.validation.constraints.*包,spring-boot-starter-validation依赖也已引入,这部分无需调整。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 01:42:03