1. 概述

在本教程中,我们将讨论 Java 应用程序服务层中的 Spring 验证。尽管 Spring Boot 支持与自定义验证器无缝集成,但执行验证的事实上的标准是Hibernate Validator

在这里,我们将学习如何将验证逻辑从控制器移至单独的服务层。此外,我们将在 Spring 应用程序的服务层中实现验证。

2. 应用分层

根据需求,Java 业务应用程序可以采用多种不同的形式和类型。例如,我们必须根据这些标准确定我们的应用程序需要哪些层。除非有特定需求,否则许多应用程序不会从服务或存储层增加的复杂性和维护成本中受益。

我们可以通过使用多层来解决所有这些问题。这些层是:

Consumer 层或 Web 层是 Web 应用程序的最顶层。 它负责解释用户的输入并提供适当的响应 。其他层抛出的异常也必须由Web层处理。由于 Web 层是我们应用程序的入口点,因此它负责身份验证并作为防止未经授权的用户的第一道防线。

Web层下面是Service层。它充当交易障碍并容纳应用程序和基础设施服务。此外,服务层的公共API由应用服务提供。 它们通常充当交易边界并负责授权交易 。基础设施服务提供连接到外部工具(包括文件系统、数据库和电子邮件服务器)的“管道代码”。这些方法经常被多个应用程序服务使用。

Web 应用程序的最低层是持久层。 换句话说,它负责与用户的数据存储进行交互。

3. 服务层验证

服务层是应用程序中促进控制器和持久层之间通信的层。此外,业务逻辑存储在服务层中。它特别包括验证逻辑。模型状态用于在控制器和服务层之间进行通信。

将验证视为业务逻辑有优点也有缺点,Spring 的验证(和数据绑定)架构也不排除这两种情况。 特别是,验证不应该绑定到 Web 层,应该易于本地化,并且应该允许使用任何可用的验证器。

此外,客户端输入数据并不总是通过 REST 控制器进程, 如果我们不在服务层中进行验证,则不可接受的数据可能会通过,从而导致一些问题 。在本例中, 我们将使用标准 Java JSR-303 验证方案

4. 示例

让我们考虑一个使用 Spring Boot 开发的简单用户帐户注册表单。

4.1.简单域类

首先,我们只有姓名、年龄、电话和密码属性:

public class UserAccount {

    @NotNull(message = "Password must be between 4 to 15 characters")
    @Size(min = 4, max = 15)
    private String password;

    @NotBlank(message = "Name must not be blank")
    private String name;

    @Min(value = 18, message = "Age should not be less than 18")
    private int age;

    @NotBlank(message = "Phone must not be blank")
    private String phone;
    
    // standard constructors / setters / getters / toString
}

在上面的类中,我们使用了四个注释 – @NotNull@Size@NotBlank@Min – 以确保输入属性既不为 null 也不为空白,并且符合大小要求。

4.2.在服务层实现验证

有许多可用的验证解决方案,由 Spring 或 Hibernate 处理实际的验证。 另一方面,手动验证是一种可行的替代方案 。当将验证集成到我们应用程序的正确部分时,这给了我们很大的灵活性。

接下来,让我们在服务类中实现验证:

@Service
public class UserAccountService {

    @Autowired
    private Validator validator;
    
    @Autowired
    private UserAccountDao dao;
    
    public String addUserAccount(UserAccount useraccount) {
        
        Set<ConstraintViolation<UserAccount>> violations = validator.validate(useraccount);

        if (!violations.isEmpty()) {
            StringBuilder sb = new StringBuilder();
            for (ConstraintViolation<UserAccount> constraintViolation : violations) {
                sb.append(constraintViolation.getMessage());
            }
            dao.addUserAccount(useraccount);
            throw new ConstraintViolationException("Error occurred: " + sb.toString(), violations);
        }         
        return "Account for " + useraccount.getName() + " Added!";
    }
}

Validator 是 Bean Validation API 的一部分,负责验证 Java 对象 。此外,Spring 自动提供了一个 Validator 实例,我们可以将其注入到 UserAccountService 中。 验证器 用于验证 validate(..) 函数中传递的对象。结果是一 ConstraintViolation

如果没有违反验证约束(对象有效),则 Set 为空。否则,我们会抛出 ConstraintViolationException

4.3.实施 REST 控制器

之后,让我们构建 Spring REST Controller 类以向客户端或最终用户显示服务并评估应用程序的输入验证:

@RestController
public class UserAccountController {

    @Autowired
    private UserAccountService service;

    @PostMapping("/addUserAccount")
    public Object addUserAccount(@RequestBody UserAccount userAccount) {
        return service.addUserAccount(userAccount);
    }
}

我们没有在上面的 REST 控制器表单中使用 @Valid 注释来阻止任何验证。

4.4.测试 REST 控制器

现在,让我们通过运行 Spring Boot 应用程序来测试此方法。之后,使用 Postman 或任何其他 API 测试工具,我们将 JSON 输入发布到 localhost:8080/addUserAccount URL: