如何使用Hibernate Validator验证重写的方法参数?

jsc*_*man 7 java validation constraints hibernate-validator

关于这个文档,我理解如果我有我的GroupService实现GroupManager并覆盖它的方法,那么我不能用验证约束进行注释,因为Hibernate Validator不允许它(结果证明它被称为Liskov替换原则).我的意思是做类似的事情

public class GroupService implements GroupManager{

    @Override
    public List<String> findUsersInGroup(@NotNull String groupName) {
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

然后ConstraintDeclarationException会被提出来,对吧?所以解决方案显然是将这些约束放在接口上,但在这种情况下:

  1. 我可能无法访问modificate接口(因为这种情况GroupManager属于Spring Security).那怎么办呢?
  2. 我认为这些验证约束不应该影响接口,因为它们是它的实现的一部分,所以如果我想要任何其他服务实现,我不应该将它连接到这些验证.也许有了这个新的,我想要实现另一种验证,并Hibernate Validator迫使我"弄脏"界面

Ren*_*ink 8

我可能无法访问修改接口(在这种情况下 GroupManager 属于 Spring Security)。在这种情况下我该怎么办?

您可以使用 xml 配置,因为 JSR-303(Bean 验证)支持它。例如

<constraint-mappings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                     xsi:schemaLocation="http://jboss.org/xml/ns/javax/validation/mapping validation-mapping-1.0.xsd"
                     xmlns="http://jboss.org/xml/ns/javax/validation/mapping">
    <default-package>org.springframework.security.provisioning</default-package>
    <bean class="GroupManager" ignore-annotations="true">
        <method name="findUsersInGroup">
            <parameter type="java.lang.String">
                <constraint annotation="javax.validation.constraints.NotNull"/>
            </parameter>
        </method>
    </bean>
</constraint-mappings>
Run Code Online (Sandbox Code Playgroud)

请参阅hibernate 文档中的 xml 配置章节。

我认为这些验证约束不应该影响接口,因为它们是其实现的一部分

正如休眠文档所说

当一个方法在子类型中被覆盖时,方法参数约束只能在基类型中声明。这种限制的原因是类型的客户端要满足先决条件不能在子类型中得到加强(甚至可能不被基类型的客户端知道)。

方法的前提条件不应通过子类型来加强。如果您说您的子类型GroupService不允许使用null参数,您可能会加强前提条件。例如,使用 的客户端GroupManager可能不知道(也不应该知道)它是GroupService. 该GroupManager接口不会对参数的任何限制。因此,如果您这样做,就会破坏以前合法的客户端代码。这违反了Liskov 替换原则

遗憾的是,GroupManagerjavadoc 没有限制参数。因此,法律实施必须处理所有情况。

一般来说......当我定义方法时,我会应用这些规则

  • 如果一个方法定义了一个参数,那么它一定不是null
  • 如果参数是可选的 - 创建重载方法并在内部处理可选参数。例如,通过使用空对象模式

这些简单的规则帮助我为客户创建一个清晰的 api。

编辑

我认为可能我有 impl "A" 和 impl "B"(都实现了相同的接口),其中 impl "A" 比 "B" 有更多(和不同)的验证

如果是这样,它们就没有相同的接口或 API。您看到的是,两者都具有相同的方法签名。但是两个相等的方法签名不能共享同一个 api 合约。当我谈论接口时,我想到的是合同,而不仅仅是签名。想象一下下面的界面:

public class Container {

    /**
     * @return a non-empty collection of elements. 
     */
     public Collection<Element> getElements();
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,合法的客户代码将是

Container container = ....;
Element firstElement = container.getElements().iterator().next();
Run Code Online (Sandbox Code Playgroud)

因为合约说它返回一个非空的集合。

如果我们更改 javadoc 并因此更改后置条件...

 /**
  * @return a collection of elements. 
  */
  public Collection<Element> getElements();
Run Code Online (Sandbox Code Playgroud)

以前合法的客户端代码将不再起作用。

我只是做了这个例子来向你展示契约和方法签名之间的区别。

可以在此处找到详细且非常好的解释。

由于 GroupManager javadoc 不限制参数,合法的实现必须处理所有情况'?这是验证方法中的参数吗?

是的。如果接口没有对参数添加任何限制,则实现必须处理每个状态,因为客户端可能会在任何状态下传递参数。