1

Introduction: I'm using Java with Spring boot 2.2.2 and Lombok

I got this example class:

package com.example.demo.classz;


import com.example.demo.pack.MyAnnotation;
import lombok.Data;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;

@Data
@RequiredArgsConstructor
public class FooClass {

    @NonNull
    @MyAnnotation
    private String str;

    private String uninterceptedString;

}

I would like to intercept all call to "get"/"set" methods that are annotated with @MyAnnotation. To manage this i created this interface:

package com.example.demo.pack;

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.FIELD)
public @interface MyAnnotation {}

And this class to do some operations.

package com.example.demo.pack;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Slf4j
@Aspect
@Component
public class AnnotatedFieldAspect {

    @Before("execution(* com.example.demo.classz.*.get*(..)) && @annotation(com.example.demo.pack.MyAnnotation)")
    public void interceptRead(JoinPoint thisJoinPoint) {
        log.info(thisJoinPoint.toString());
    }

    @Before("execution(* com.example.demo.classz.*.set*(..)) && @annotation(com.example.demo.pack.MyAnnotation) && args(newValue)")
    public void interceptWrite(JoinPoint thisJoinPoint, Object newValue) {
        log.info(thisJoinPoint + " -> " + newValue);
    }
}

Finally to test all, i made a simple controller

package com.example.demo;


import com.example.demo.classz.FooClass;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MyController {

    @GetMapping("/")
    public String test() {
        FooClass fooClass = new FooClass("Foo");

        fooClass.setStr("Foo2");
        fooClass.getStr();

        return "Hi";
    }
}

I can't manage to activate those pointcut and I can't understand why. Could you help me?

I already saw some similar question on StackOverflow like this: - Spring AOP Pointcut expression for annotated field - @AspectJ pointcut for all methods of a class with specific annotation

And some other, but even with their solution i did not succeed.

Some config stuff:

@EnableAspectJAutoProxy
@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

And dependecies section from build.gradle:

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-actuator'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.session:spring-session-core'
    compile group: 'org.springframework.boot', name: 'spring-boot-starter-aop', version: '2.2.2.RELEASE'
//  compile group: 'org.aspectj', name: 'aspectjweaver', version: '1.9.5'
    compileOnly 'org.projectlombok:lombok'
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    annotationProcessor 'org.projectlombok:lombok'
    providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
}

Thanks!

ErikM
  • 519
  • 5
  • 14
  • 1
    Your `FooClass` must be a Spring component in order to be intercepted by Spring AOP. Is it? I see no `@Component` annotation there. – kriegaex Dec 11 '19 at 00:30
  • No, there isn't and I can't insert it because my FooClass has a constructor. I was reading about AspectJ that should fix this problem, but I don't know yet which changes should I do. – ErikM Dec 11 '19 at 09:49
  • maybe a small but relevant detail: you set your annotation on field level, but try to intercept (get/set)"methods"! – xerx593 Dec 11 '19 at 15:36

1 Answers1

2

As kriegaex mentioned, the FooClass is not a bean managed by Spring Container. You can only intercept a spring bean.

Following changes to the code changes will make the setter methods to be intercepted provided the pointcut expressions are valid.

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import lombok.Data;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;

    @Data
    @RequiredArgsConstructor
    @Component
    @Scope(scopeName="prototype")
    public class FooClass {

        @NonNull
        @MyAnnotation
        private String str;

        private String uninterceptedString;

    }

and obtain the FooClass bean from the application context.

FooClass fooClass = ctx.getBean(FooClass.class,"Foo");
fooClass.setStr("Foo2");
fooClass.getStr();

Read more on @Component and @Scope to understand the changes done.

@Component @Scope

Note that only method invocations can be intercepted with Spring AOP. Read docs here

== Spring AOP Capabilities and Goals

Spring AOP currently supports only method execution join points (advising the execution of methods on Spring beans). Field interception is not implemented, although support for field interception could be added without breaking the core Spring AOP APIs. If you need to advise field access and update join points, consider a language such as AspectJ.

For the same reason the following Spring AOP Pointcut expression would be valid in your case

@Before("execution(* com.example.demo.classz.*.get*(..))")
    public void interceptRead(JoinPoint thisJoinPoint) {
        log.info(thisJoinPoint.toString());
    }

    @Before("execution(* com.example.demo.classz.*.set*(..)) && args(newValue)")
    public void interceptWrite(JoinPoint thisJoinPoint, Object newValue) {
        log.info(thisJoinPoint + " -> " + newValue);
    }
R.G
  • 6,436
  • 3
  • 19
  • 28