0

I want to write an annotation in Java, which executes something before and after execution of the annotated method, similar to what can be done in Spring with aspects.

I've already tried a Spring aspect, but it only works for Beans (as this guy here mentioned) and I want to stay independent from the Spring framework.

A simple class that writes a String to the console:

public class Main {
    public static void main(String[] args) {
        say("How are you?");
    }

    @Hello
    private static void say(String s) {
        System.out.println(s);
    }
}

The associated annotation:

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

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Hello {}

And I need something like (deduced from the Spring aspect)

public Object process(ProceedingJoinPoint proceedingJoinPoint) {
    System.out.println("Hello");
    Object proceed = null;
    try {
        proceed = proceedingJoinPoint.proceed();
    } catch (Throwable throwable) {
        throwable.printStackTrace();
    }
    System.out.println("world!");
    return proceed;
}

I want the following output:

Hello

How are you?

world!

Edit:

I created the following Aspect (without annotations), but it does not work

@lombok.extern.java.Log
public aspect Log {
    pointcut methodExecuted():execution(* **(..));

    void around(): methodExecuted() {
        log.info("Hello");
        proceed();
        log.info("world!");
    }
}

Where is my mistake?

Community
  • 1
  • 1
Andreas
  • 430
  • 5
  • 21
  • Well, you need something to intercept those method calls. Java won't do that out of the box so you'll need to "weave in" some itercepting code, i.e. you need an [AOP](https://en.wikipedia.org/wiki/Aspect-oriented_programming) framework. If you don't want to or can't use Spring AOP have a look at AspectJ. – Thomas Jun 12 '19 at 15:30
  • As I said, with Spring I have the problem that the annotation only works with beans or did I misunderstand something? – Andreas Jun 12 '19 at 15:33
  • Well, in theory almost everything can be a bean. Beans would enable the framework to wrap the instances in proxies that intercept the calls which makes it easier to use. For non-bean classes, or "beans" that aren't instantiated by the framework, you'd need to weave the intercepting code into the class itself - which can happen at compile or load time. Have a look here for some information on how AOP (with load time weaving) can be done with Spring: https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#aop – Thomas Jun 13 '19 at 07:51
  • I kept to [that](https://stackoverflow.com/a/8843713/10781526) answer, but it doesn't work. I edited my question accordingly. – Andreas Jun 13 '19 at 08:05
  • 1
    It isn't limited to beans, it is currently limited because you are using Spring AOP which is proxy based and that only works for beans. You can use full blown aspectj with either compile or loadtime weaving which actually modifies the bytecode and actually injects code in there, which works with and without Spring. The aspeects you write are already AspectJ based aspects and have nothing to do with Spring. – M. Deinum Jun 13 '19 at 08:29
  • Can you tell me exactly what I need to do? – Andreas Jun 13 '19 at 08:30

1 Answers1

1

Assuming that you successfully compiled your aspect with the AspectJ compiler, it should work with the code you used, only that it would log all method executions, i.e. also main(..), so you would se the aspect's output twice before and after "How are you?". If you don't see anything probably you made a mistake in setting up your build system.

You should change the pointcut to actually limit logging to annotated methods: execution(* *(..)) && @annotation(Hello). Furthermore if your around advice has a void return type, logging will not work with non-void methods. So you should rather use a return type of Object and actually return the result of proceed().

I also strongly urge you to not just blindly wield a powerful tool like AspectJ but also study some documentation before you do. It is quite obvious that you have not done so or only very cursorily. Then you get the effect of being a not-so-capable-user with a tool. ;-)

Here is my MCVE:

package de.scrum_master.app;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

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

@Retention(RUNTIME)
@Target(METHOD)
public @interface Hello {}
package de.scrum_master.app;

public class Application {
  public static void main(String[] args) {
    say("How are you?");
  }

  @Hello
  private static void say(String s) {
    System.out.println(s);
  }
}
package de.scrum_master.aspect;

import de.scrum_master.app.Hello;

public aspect LoggingAspect {
  pointcut methodExecuted() : execution(* *(..)) && @annotation(Hello);

  Object around(): methodExecuted() {
    System.out.println("Hello");
    Object result = proceed();
    System.out.println("world!");
    return result;
  }
}

Console log:

Hello
How are you?
world!
kriegaex
  • 63,017
  • 15
  • 111
  • 202
  • You don't need an aspect in the AspectJ language the `@Aspect` one would work equally well. – M. Deinum Jun 13 '19 at 11:50
  • You say that as if it was an advantage. IMO it is not. The native syntax is more expressive and does not try to put everything into annotations. Besides, the OP used native syntax in his own example, I just stuck with it, happy to see something (for me) more readable for a change. I have no idea why @AspectJ syntax is so popular even outside of Spring AOP where it is the only choice. – kriegaex Jun 14 '19 at 00:13
  • The OP started with a Java based example and was under the impression that it was Spring only, I tried to prevent/explain that this wasn't the case, hence my suggestion to use plain Java with annotations. I don't really mind the annotations in the AspectJ classes, added advantage is that you don't need a specialized compiler for it. On the other hand declaring precedence and introductions is far easier in native syntax imho. – M. Deinum Jun 17 '19 at 06:11
  • 1
    M. Deinum, let me qualify your general statement a bit: If you don't compile an @AspectJ aspect with the AspectJ compiler, it is **unfinished**, i.e. you can only use it in an LTW (load-time weaving) scenario because the weaving agent needs to not just weave the aspect but also finish the pure Java class to be a full-fledged aspect class before weaving it. This again means you need to be able to control the JVM command line. If you want to use CTW (compile-time weaving) there is no way around using the AspectJ compiler, not matter which syntax variant you use. – kriegaex Jun 18 '19 at 00:36