Search code examples
javaaopaspectjspring-annotationsjava-annotations

Custom Java annotation to skip the method execution


I want to create a custom annotation to skip method execution

This is my annotation code, with the validator class

@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{};

}

I tried it with validator. This is how my validator looks like

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;
    }

}

And this is how I want to use -- I want to use the annotation on method level and to skip the method execution.

I don't know if it is possible.. Please help.

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;


    }
}

Solution

  • It is actually very simple, sort of the simplest aspect one can write. ;-)

    The ugly thing about your sample code is that it uses several classes for which you do not show the source code, so I had to create dummy classes/interfaces in order to make your code compile. You also do not show how the validator is applied, so I have to speculate. Anyway, here is a fully self-consistent set of sample classes:

    Helper classes:

    This is just scaffolding in order to make everything compile.

    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 {};
    }
    

    Driver application:

    If you want to test something, you do not just need a positive test case, but also a negative one. Because you did not provide that, user Sampisa's answer was not what you were looking for. BTW, I think you should have been able to deduce from it the solution by yourself. You did not even try. Do you not have any programming experience?

    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;
      }
    }
    

    Aspect:

    The only reason why I add another sample aspect in addition to Sampisa's is that his solution is suboptimal with regard to his reflection usage. It is ugly and it is slow. I think my solution is a bit more elegant. See for yourself:

    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;
      }
    }
    

    Very simple, is it not?

    Console log:

    true
    false
    

    Et voilà - I think this is what you were looking for.