1

Could someone please help me understand the below pointcut in aspectj?

This pointcut works for annotation @DynamicValue but how it works is confusing.

Below is my custom annotation:

@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface DynamicValue {
String lookupName() default "";
}

My class using the annotation:

com.package.sample;
Class SampleA{

@DynamicValue("Hello")
public String greet;

//getters & setters

}

And the aspect containing pointcut:

@Aspect
@Configurable
@Component
public class DynamicValueAspect
{

@Pointcut("get(@com.sample.aspect.DynamicValue java.lang.String com.package.sample..*.*)")
public void isDynamicValue() {}

@Around("isDynamicValue()")
public void getLocalized(ProceedingJoinPoint pjp) throws Throwable {
    System.out.println("Annotation called");
//How can I access the parameter in annotation here 
//i.e "Hello" from the @DynamicValue("Hello")
}

Is the pointcut correct, and will it work wherever I use the @DynamicValue annotation?

Also I would like to know how to access the Annotation Parameter in the advice?

Don't Panic
  • 41,125
  • 10
  • 61
  • 80

2 Answers2

2

Annotation:

package de.scrum_master.app;

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

@Target({ ElementType.FIELD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface DynamicValue {
    String lookupName() default "";
}

Driver application with an annotated member:

package de.scrum_master.app;

public class Application {
    @DynamicValue(lookupName = "foobar")
    public String greet;
    public String anotherMember;

    public static void main(String[] args) {
        Application application = new Application();
        application.greet = "world";
        application.anotherMember = "Another member";
        application.sayHello();
    }

    private void sayHello() {
        System.out.println(anotherMember);
        System.out.println("Hello " + greet);
    }
}

Aspect:

package de.scrum_master.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.stereotype.Component;

import de.scrum_master.app.DynamicValue;

@Aspect
@Configurable
@Component
public class DynamicValueAspect {
    @Pointcut("get(String de.scrum_master.app..*) && @annotation(dynamicValue)")
    public void isDynamicValue(DynamicValue dynamicValue) {}

    @Around("isDynamicValue(dynamicValue)")
    public Object getLocalized(DynamicValue dynamicValue, ProceedingJoinPoint thisJoinPoint) throws Throwable {
        System.out.println(thisJoinPoint);
        System.out.println("    " + dynamicValue);
        return thisJoinPoint.proceed();
    }
}

Console log:

Another member
get(String de.scrum_master.app.Application.greet)
    @de.scrum_master.app.DynamicValue(lookupName=foobar)
Hello world

By the way, a @Before advice is sufficient if you do not want to change the result for get(). For just printing something @Around is overkill.


Because OP asked if it is possible to limit pointcut matching to a certain primitive type like int, both capturing real primitives and the boxed type Integer, I am going to show a variant doing just that. As as I said earlier, if there is not need to manipulate the value returned by the field accessor pointcut, there is no need to use @Around either. We just use @AfterReturning because it has an optional returning parameter by means of which we can bind the actual return value to an advice parameter. If then we choose something other than Object for that advice parameter - in this case we just use int - we get what was asked for.

So let us add two annotated parameters typed int and Integer to the original code:

Driver application with annotated members:

package de.scrum_master.app;

public class Application {
    @DynamicValue(lookupName = "foobar")
    public String greet;
    public String anotherMember;
    @DynamicValue(lookupName = "primitive")
    public int primitiveNumber;
    @DynamicValue(lookupName = "boxed")
    public Integer boxedNumber;

    public static void main(String[] args) {
        Application application = new Application();
        application.greet = "world";
        application.anotherMember = "Another member";
        application.primitiveNumber = 11;
        application.boxedNumber = 22;
        application.sayHello();
    }

    private void sayHello() {
        System.out.println(anotherMember);
        System.out.println("Hello " + greet);
        System.out.println(primitiveNumber);
        System.out.println(boxedNumber);
    }
}

Mofified aspect targetting int/Integer field accessors only:

package de.scrum_master.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.stereotype.Component;

import de.scrum_master.app.DynamicValue;

@Aspect
@Configurable
@Component
public class DynamicValueAspect {
    @Pointcut("get(* de.scrum_master.app..*) && @annotation(dynamicValue)")
    public void isDynamicValue(DynamicValue dynamicValue) {}

    @AfterReturning(pointcut = "isDynamicValue(dynamicValue)", returning = "field")
    public void getLocalized(DynamicValue dynamicValue, int field, JoinPoint thisJoinPoint) throws Throwable {
        System.out.println(thisJoinPoint);
        System.out.println("    " + dynamicValue);
    }
}

New console log:

Another member
Hello world
get(int de.scrum_master.app.Application.primitiveNumber)
    @de.scrum_master.app.DynamicValue(lookupName=primitive)
11
get(Integer de.scrum_master.app.Application.boxedNumber)
    @de.scrum_master.app.DynamicValue(lookupName=boxed)
22

BTW, I answered a similar question here.

Community
  • 1
  • 1
kriegaex
  • 63,017
  • 15
  • 111
  • 202
  • Thanks a lot. That worked like charm. 1) Can I create a pointcut that supports both primitive and wrapper return type? Eg: @Pointcut("get(Integer de.scrum_master.app..*) || get(int de.scrum_master.app..*) && @annotation(dynamicValue)") 2) Will the pointcut work if I just print the class variable without calling its getter? Thanks again for the help you've already did. :) – Yadvendra Rathore Dec 05 '16 at 09:05
  • Could you please look at this question? http://stackoverflow.com/questions/40991659/pointcut-get-expression-not-working-in-xml Thanks in advance. You're the savior. – Yadvendra Rathore Dec 07 '16 at 05:34
  • @YadvendraRathore, I have updated my answer so as to address your first question. – kriegaex Dec 10 '16 at 12:20
  • Hi, Thanks for the solution. Your solution worked perfectly, but now I'm stuck at some other point. Have posted this question 5 days ago & got no answer. Could you please help me on http://stackoverflow.com/questions/40991659/pointcut-get-expression-not-working-in-xml this question. Maybe it's just you who can help. Thanks – Yadvendra Rathore Dec 10 '16 at 12:28
0

Change your method signature as follows:

@Around("isDynamicValue()")
public void getLocalized(ProceedingJoinPoint pjp, DynamicValue dynamicValue) throws Throwable {
    System.out.println(dynamic.lookupName());
}

I didn't quite understand if you also have problems with the pointcut. Here is an example that should work though

@Around("execution(@com.package.sample.DynamicValue * *(..)) && @annotation(dynamicValue)")
public void isDynamicValue() {}
Vaelyr
  • 2,841
  • 2
  • 21
  • 34
  • Thanks, I'm trying your concept to retrieve the annotation parameter but facing issue in retrieving in @around advice @Pointcut("get(@com.util.DynamicValue java.lang.String com.sample.package.*.*)") public void isDynamicValue() {}
    @Around("isDynamicValue()") public String getLocalized(ProceedingJoinPoint pjp,DynamicValue dynamicValue) throws Throwable It gives errror formal unbound in pointcut at 2nd argument in advice method
    – Yadvendra Rathore Dec 03 '16 at 18:57
  • And what is the issue? – Vaelyr Dec 03 '16 at 19:18
  • it gives error "formal unbound in pointcut" in 2nd argument of @Around advice – Yadvendra Rathore Dec 04 '16 at 03:55
  • 2
    `execution()` is the wrong pointcut in this case because the OP wants to use `get()` so as to intercept member access. – kriegaex Dec 04 '16 at 14:44