【问题标题】:Custom Java annotation to skip the method execution自定义 Java 注释以跳过方法执行
【发布时间】:2017-07-19 10:45:14
【问题描述】:

我想创建一个自定义注解来跳过方法执行

这是我的注释代码,带有验证器类

@Target({ METHOD , FIELD , PARAMETER } )
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy={MyValidator .class})
public @interface MyAnnotation {

    String message() default "DEFAULT_FALSE";

    Class<?>[] groups() default{};

    Class<? extends Payload>[] payload() default{};

}

我用验证器试了一下。这就是我的验证器的样子

public class MyValidator implements ConstraintValidator<MyAnnotation, String >{

    @Override
    public void initialize(MyAnnotation arg0) {

    }

    @Override
    public boolean isValid(String arg0, ConstraintValidatorContext arg1) {

        if(str=="msg"){
            return true;
        }
        return false;
    }

}

这就是我想使用的方式——我想在方法级别使用注释并跳过方法执行。

我不知道这是否可能..请帮助。

public class Test {



    public static void main(String[] args) {
    Test t = new Test();
        boolean valid=false;

         valid=t.validate();
        System.out.println(valid);

    }

@MyAnnotation(message="msg")
    public boolean validate(){

     // some code to return true or false
    return true;


    }
}

【问题讨论】:

  • 您需要将if(str=="msg"){更改为使用equals()
  • “跳过”的事情会如何发生?
  • 嗨,是的,这就是我的问题,如果可以使用这样的验证器,我不知道是否可以完成
  • @SharonBenAsher,请建议是否有其他方法,感谢您的帮助:)
  • 如果您已经知道要跳过方法执行 - 并且您必须知道是否要事先注释目标方法 - 为什么要调用或实现它们?我知道如何通过 AspectJ 轻松实现它,但这种方法本身是有缺陷的。问题仍然存在,如果跳过非 void 方法应该返回什么?举一个更合理的例子,然后我可能会愿意提供帮助。我需要了解您要解决的实际问题,而不是您认为应该如何解决。这两个不一定相同。

标签: java aop aspectj spring-annotations java-annotations


【解决方案1】:

您应该为此使用 AOP。创建一个 AspectJ 项目,然后尝试如下操作:

MyAnnotation.java:

package moo.aspecttest;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(value = { ElementType.METHOD })
public @interface MyAnnotation
{
    public String value();
}

MyAspectClass.java:

package moo.aspecttest;

import java.lang.reflect.Method;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;

@Aspect
public class MyAspectClass
{

    @Around("execution(* *(..))")
    public Object aroundAdvice(ProceedingJoinPoint point) throws Throwable
    {
        Method method = MethodSignature.class.cast(point.getSignature()).getMethod();
        String name = method.getName();
        MyAnnotation puff = method.getAnnotation(MyAnnotation.class);
        if (puff != null) {
            System.out.println("Method " + name + " annotated with " + puff.value() + ": skipped");
            return null;
        } else {
            System.out.println("Method " + name + " non annotated: executing...");
            Object toret = point.proceed();
            System.out.println("Method " + name + " non annotated: executed");
            return toret;
        }
    }
}

MyTestClass.java:

package moo.aspecttest;

public class MyTestClass
{

    @MyAnnotation("doh")
    public boolean validate(String s) {
        System.out.println("Validating "+s);
        return true;
    }

    public boolean validate2(String s) {
        System.out.println("Validating2 "+s);
        return true;
    }

    public static void main(String[] args)
    {
        MyTestClass mc = new MyTestClass();

        mc.validate("hello");
        mc.validate2("cheers");

        }
    }
}

运行时生成的输出:

Method main non annotated: executing...
Method validate annotated with doh: skipped
Method validate2 non annotated: executing...
Validating2 cheers
Method validate2 non annotated: executed
Method main non annotated: executed

我使用了一个非常通用的 aroundAdvice,但如果你愿意,你可以使用 beforeAdvice。确实,我认为这一点很明确。

【讨论】:

  • 如果它不适合你,缺少一些东西,而不是@Sampisa。您是否可以学习如何以有意义的方式提出问题,同时传达您的实际意图?
  • 请记住将 AspectJ 依赖项/运行时库添加到您的项目中。很容易,如果您使用 Eclipse,右键单击您的项目,配置 -> 转换为 AspectJ 项目。在这里你可以找到一个简单的教程:o7planning.org/en/10257/…
  • @Sampisa,您关于可以使用@Before 建议的说法是错误的。您不能停止方法的执行(除了通过抛出异常)或更改之前通知中的返回值。
  • 因此,您是在告诉敏捷教练,您使用番茄暂停来进行重复的多任务上下文切换。嗯... ;-) 我不会告诉你的 Scrum Master。
  • @Sampisa,谢谢伙计,您的回答让我理解了方面上下文,但下面的答案按要求工作
【解决方案2】:

其实很简单,是可以写的最简单的方面。 ;-)

您的示例代码的丑陋之处在于它使用了几个您没有显示其源代码的类,因此我必须创建虚拟类/接口才能使您的代码编译。你也没有展示验证器是如何应用的,所以我不得不推测。无论如何,这是一组完全自洽的示例类:

助手类:

这只是为了让一切都可以编译的脚手架。

package de.scrum_master.app;

public interface Payload {}
package de.scrum_master.app;

public class ConstraintValidatorContext {}
package de.scrum_master.app;

public @interface Constraint {
  Class<MyValidator>[] validatedBy();
}
package de.scrum_master.app;

import java.lang.annotation.Annotation;

public interface ConstraintValidator<T1 extends Annotation, T2> {
  void initialize(T1 annotation);
  boolean isValid(T2 value, ConstraintValidatorContext validatorContext);
}
package de.scrum_master.app;

public class MyValidator implements ConstraintValidator<MyAnnotation, String> {
  @Override
  public void initialize(MyAnnotation annotation) {}

  @Override
  public boolean isValid(String value, ConstraintValidatorContext validatorContext) {
    if ("msg".equals(value))
      return true;
    return false;
  }
}
package de.scrum_master.app;

import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;

import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.*;

@Target({ METHOD, FIELD, PARAMETER })
@Retention(RUNTIME)
@Constraint(validatedBy = { MyValidator.class })
public @interface MyAnnotation {
  String message() default "DEFAULT_FALSE";
  Class<?>[] groups() default {};
  Class<? extends Payload>[] payload() default {};
}

驱动程序应用:

如果你想测试一些东西,你不仅需要一个正面的测试用例,还需要一个负面的测试用例。因为您没有提供,所以用户 Sampisa 的答案不是您想要的。顺便说一句,我认为您应该能够自己从中推断出解决方案。你甚至没有尝试。你没有任何编程经验吗?

package de.scrum_master.app;

public class Application {
  public static void main(String[] args) {
    Application application = new Application();
    System.out.println(application.validate1());
    System.out.println(application.validate2());
  }

  @MyAnnotation(message = "execute me")
  public boolean validate1() {
    return true;
  }

  @MyAnnotation(message = "msg")
  public boolean validate2() {
    return true;
  }
}

方面:

我在 Sampisa 的基础上添加另一个示例方面的唯一原因是他的解决方案在反射使用方面不是最佳的。它很丑而且很慢。我认为我的解决方案更优雅一些。自己看:

package de.scrum_master.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class SkipValidationAspect {
  @Around("execution(@de.scrum_master.app.MyAnnotation(message=\"msg\") boolean *(..))")
  public boolean skipValidation(ProceedingJoinPoint thisJoinPoint) throws Throwable {
    return false;
  }
}

很简单,不是吗?

控制台日志:

true
false

等等 - 我想这就是你要找的东西。

【讨论】:

  • aye aye Captain ... 我在 stackoverflow.com 上看到的最好的答案之一
  • 我只是稍微编辑了方面,两个答案的组合
  • 嗨@kriegaex,我在 project1 中添加了这个,并且在 project2 中使用了 project1.jar。它在 project2 中不起作用。 project1 是否需要是 AspectJ 项目 ..?并且 project2 是否也需要是 AspectJ 项目
  • 请为它创建一个新问题,它是题外话。在其中,更好地描述问题。但在您这样做之前,请先阅读一些 AspectJ 教程。其他人不可能为你做所有事情。并提前感谢您接受并支持我的回答。 P.S.:我不明白您为什么认为应该将我的简单、优雅的解决方案与另一个等效但复杂的解决方案结合起来。您认为生病时吃药更有帮助吗?
  • 你好@kriegaex - 我尝试在 validate1 方法中添加代码,它没有被执行,当我看到 exoprted jar - 使用反编译器 - 然后我看到 validate1() - 包含一些名为 - validate1_aroundBody1$advice - ,我的方法代码在 validate1_aroundBody0 中,它没有被调用,为什么...?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多