Java约束验证不适用于参数

问题描述 投票:0回答:3

我想将java bean验证批注用于spring服务的参数。考虑以下服务:

public interface MyService {

    void methodA();


    void methodB(@NotBlank String param)
}

带有实现:

@Validated
public class MyServiceImpl implements MyService {

    @Override
    public void methodA() {
        String param = "";
        methodB(param)
    }

    @Override
    public void methodB(@NotBlank String param) {
        // some logic
    }
}

您能告诉我如何在传递的字符串为空时触发验证并引发约束异常吗?当我通过这种方式致电服务时:

@Autowired
MyService myService;

myService.methodB("");

当从另一个类中调用methodB时,将按预期引发约束异常。

但是当相同的methodB称为形式MethodA时,不会引发异常。如果调用具有相同参数的相同方法,为什么不引发异常?

java spring-boot bean-validation spring-validator
3个回答
1
投票

[除了其他答案以及您知道AOP代理存在的事实,让我仅指向the relevant chapter in Spring documentation,其中提到您遇到的AOP代理的自调用问题:

public class Main {

    public static void main(String[] args) {
        ProxyFactory factory = new ProxyFactory(new SimplePojo());
        factory.addInterface(Pojo.class);
        factory.addAdvice(new RetryAdvice());

        Pojo pojo = (Pojo) factory.getProxy();
        // this is a method call on the proxy!
        pojo.foo();
    }
}

fun main() {
    val factory = ProxyFactory(SimplePojo())
    factory.addInterface(Pojo::class.java)
    factory.addAdvice(RetryAdvice())

    val pojo = factory.proxy as Pojo
    // this is a method call on the proxy!
    pojo.foo()
}

这里要理解的关键是,main(..)类的Main方法内部的客户端代码具有对代理的引用。这意味着该对象引用上的方法调用是代理上的调用。结果,代理可以委派给与该特定方法调用相关的所有拦截器(建议)。 但是,一旦调用最终到达目标对象(在这种情况下为SimplePojo,在此情况下为引用),则它可能对其自身进行的任何方法调用(例如this.bar()this.foo())都将被调用针对this参考,而不是代理。这具有重要意义。这意味着自调用不会导致与方法调用相关的建议得到执行的机会。

-https://docs.spring.io/spring/docs/5.2.3.RELEASE/spring-framework-reference/core.html#aop-understanding-aop-proxies

在下一段中,提出了两个解决方案(或者实际上是三个,但是在这种情况下切换到AspectJ可能会很麻烦):

好的,对此该怎么办?最佳方法(在这里宽松地使用术语“最佳”)是重构代码,以免发生自调用。这确实需要您做一些工作,但这是最好的,侵入性最小的方法。下一个方法绝对可怕,我们正要指出这一点,恰恰是因为它是如此可怕。您可以(对我们来说是痛苦的)将类中的逻辑完全绑定到Spring AOP,如以下示例所示:

public class SimplePojo implements Pojo {

    public void foo() {
        // this works, but... gah!
        ((Pojo) AopContext.currentProxy()).bar();
    }

    public void bar() {
        // some logic...
    }
}

class SimplePojo : Pojo {

    fun foo() {
        // this works, but... gah!
        (AopContext.currentProxy() as Pojo).bar()
    }

    fun bar() {
        // some logic...
    }
}

这将您的代码完全耦合到Spring AOP,并且使类本身意识到在AOP上下文中使用它这一事实,而AOP却是这样。创建代理时,还需要一些其他配置,如以下示例所示:

public class Main {

    public static void main(String[] args) {
        ProxyFactory factory = new ProxyFactory(new SimplePojo());
        factory.addInterface(Pojo.class);
        factory.addAdvice(new RetryAdvice());
        factory.setExposeProxy(true);

        Pojo pojo = (Pojo) factory.getProxy();
        // this is a method call on the proxy!
        pojo.foo();
    }
}

fun main() {
    val factory = ProxyFactory(SimplePojo())
    factory.addInterface(Pojo::class.java)
    factory.addAdvice(RetryAdvice())
    factory.isExposeProxy = true

    val pojo = factory.proxy as Pojo
    // this is a method call on the proxy!
    pojo.foo()
}

最后,必须注意,AspectJ没有此自调用问题,因为它不是基于代理的AOP框架。

-https://docs.spring.io/spring/docs/5.2.3.RELEASE/spring-framework-reference/core.html#aop-understanding-aop-proxies


1
投票

当托管bean调用另一个托管bean时,将调用Spring验证。

但是,Spring上下文不知道同一bean(即,内部bean而非内部bean)中的方法之间的调用,因此@Validation没有影响。

一个简单的解决方案是将包装器方法从类中移到实用程序方法中,例如:

public static void methodA(MyService myService) {
    myService.methodB("");
}

0
投票

在Spring中没有注释@ Validation。我认为您的意思是@ Validated

为了验证参数,Spring使用CGLIB创建了一种代理。这是类似于Spring用于事务处理的机制。仅当从another类调用您的类MyServiceImpl时,即两个类之间的控制流越过边界时,Spring才会添加此代码。当您从另一个类调用methodB时,Spring会添加验证代码。当您从同一类调用它时,Spring不会添加任何代码,因此不会触发任何验证。

© www.soinside.com 2019 - 2024. All rights reserved.