0

We have some code that needs to be run in a lot of methods and it's tedious for our developers to have to write this over and over (and also we want to allow for the APIs called to be able to change without the need to change any code that would use it).

Our idea is to put a custom annotation on methods that would call this code, and we would write our own Annotation processing code that would look for that annotation and then add the code to the end of the method prior to compilation (but when they look in the IDE at the file after this, the code still wouldn't be there).

How would I achieve this? What would I need to do to make something called by Gradle, able to alter the method definitions passed to the compiler/builder and able to read the annotations on the methods?

(We are using Spring Boot as well as Gradle, but that might not make a difference)

Don Rhummy
  • 24,730
  • 42
  • 175
  • 330
  • 1
    You don't have to use an annotation processor. Can use Spring AOP if it's simple enough. Have a look at a simple sample: https://stackoverflow.com/questions/36892410/springboot-logback-configuration or can implement a custom annotation too:http://www.baeldung.com/spring-aop-annotation It is MUCH simpler to implement and configure than implementing a compile time annotation processor. – Strelok Aug 02 '17 at 05:40

1 Answers1

1

Spring AOP is good enough to accomplish your requirement.

This is an small example to give you an idea: There are two classes and each one have three methods in common: play(), addPlayer() and gameover(), and each time that the play method is called the program have to call a routine to print a text, with AOP you don't need to repeat the same code.

For organization order I will use an interface, it is not mandatory but it's a good practice:

Game Interface:

public interface Game {
    void play();
    void addPlayer(String name);
    void gameOver();
}

A soccer class that implements Game

public class Soccer implements Game {
    @Override
    public void play() {
        System.out.println("Soccer Play started");
    }

    @Override
    public void addPlayer(String name) {
        System.out.println("New Soccer Player added:" + name);
    }

    @Override
    public void gameOver() {
        System.out.println("This soccer Game is Over");
    }
}

A Baseball class that implements Game

public class Baseball implements Game {
    @Override
    public void play() {
        System.out.println("Baseball game started at " + new Date());
    }

    @Override
    public void addPlayer(String name) {
        System.out.println("New Baseball Player added: " +name);
    }

    @Override
    public void gameOver() {
        System.out.println("The game is over");
    }
}

Now the Aspect configuration to catch when the play method is called

@Aspect
@Component
public class AspectConfiguration {

    @Before("execution(* org.boot.aop.aopapp.interfaces.Game.play(..))")
    public void callBefore(JoinPoint joinPoint){
        System.out.println("Do this allways");
        System.out.println("Method executed: " + joinPoint.getSignature().getName());
        System.out.println("******");
    }
}

The @Before annotation means that the method will be called before play method is executed. Also you need to specify a pointcut expression the tell Aspect how to match the method call that you need to trigger. For example in this case we use the play method and it is the pointcut expression: "execution(* org.boot.aop.aopapp.interfaces.Game.play(..))"

And finally the Spring Boot Application Class:

@EnableAspectJAutoProxy
@SpringBootApplication
public class AopappApplication {

    public static void main(String[] args) {

        Game soccer=null;

        Game baseball=null;

        AnnotationConfigApplicationContext ctx = (AnnotationConfigApplicationContext) SpringApplication.run(AopappApplication.class, args);
        soccer = (Game) ctx.getBean("soccer");
        baseball = (Game) ctx.getBean("baseball");

        soccer.play();
        baseball.play();

        soccer.addPlayer("Player 1");
        soccer.addPlayer("Player 2");

        baseball.addPlayer("Player 23");

        soccer.gameOver();
        baseball.gameOver();
    }

    @Bean("soccer")
    public Game soccer(){
        return new Soccer();
    }

    @Bean("baseball")
    public Game baseball(){
        return new Baseball();
    }
}

There is a god documentation about Spring AOP plese see the following link. Spring AOP Doc.

Daniel C.
  • 5,418
  • 3
  • 23
  • 26
  • Is there a way to get access to any objects used inside the method? (Assuming I know their variable names before hand?) – Don Rhummy Aug 02 '17 at 18:50
  • Sure you can. see this post it shows how access the method arguments: https://stackoverflow.com/questions/15660535/get-method-arguments-using-spring-aop – Daniel C. Aug 02 '17 at 19:07
  • The option that I sent you is just how read the method arguments. But for local vaiables there is no option to read them. – Daniel C. Aug 02 '17 at 20:05
  • Catch the returned value works for you? because there is @AfterReturning annotation so you can read the returned value of your method. – Daniel C. Aug 02 '17 at 20:37