Spring AOP introductions not working with && this(IntroducedInterface) - spring

I have following aspect
#Aspect
public class AspectClass{
#DeclareParents(value="com.mac.model.*",defaultImpl=Impl.class)
public IntroduceInterface inter;
#Pointcut("execution(* com.mac.Employee.display(..))")
public void empPointcut(){}
#Before("empPointCut() && this(introduceInterface)")
public void BeforeAdvice(JoinPoint jp,IntroduceInterface inf){
inf.introMethod();
}
}
and I am trying to replicate code from spring docs which is like below:
#Aspect
public class UsageTracking {
#DeclareParents(value="com.xzy.myapp.service.*+", defaultImpl=DefaultUsageTracked.class)
public static UsageTracked mixin;
#Before("com.xyz.myapp.SystemArchitecture.businessService() && this(usageTracked)")
public void recordUsage(UsageTracked usageTracked) {
usageTracked.incrementUseCount();
}
}
but its not working its giving error :
IllegalArgumentException error at ::0 formal unbound in pointcut
Its a simple spring application .What could be reason its not working?

Name in here
#Before("empPointCut() && this(_name_in_here_)")
should be the same as in
public void BeforeAdvice(JoinPoint jp,IntroduceInterface _name_in_here_){
So this should work fine:
#Aspect
public class AspectClass{
#DeclareParents(value="com.mac.model.*",defaultImpl=Impl.class)
public IntroduceInterface inter;
#Pointcut("execution(* com.mac.Employee.display(..))")
public void empPointcut(){}
#Before("empPointCut() && this(inf)")
public void BeforeAdvice(JoinPoint jp,IntroduceInterface inf){
inf.introMethod();
}
}

Annotation object is available to the advice via the annotation parameter. So both names (method parameter name and annotation declared inside pointcut expression) should be same.
Please take a look at these two sections from spring documentation - Passing parameters to advice.
From Spring doc:
The proxy object ( this), target object ( target), and annotations ( #within, #target, #annotation, #args) can all be bound in a similar fashion. The following example shows how you could match the execution of methods annotated with an #Auditable annotation, and extract the audit code.
First the definition of the #Auditable annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Auditable {
AuditCode value();
}
And then the advice that matches the execution of #Auditable methods:
#Before("com.xyz.lib.Pointcuts.anyPublicMethod() && #annotation(**auditable**)")
public void audit(Auditable **auditable**) {
AuditCode code = auditable.value();
// ...
}

Related

advice is not executing if the annotations contains parameters

I'm trying to get some advice to execute.
When I use annotation without parameters its do execute but when the annotation includes parameters it's not.
#Aspect
class a{
#Pointcut("execution(#com.annotations.AnnotationName* *(..))")
void someMethod() {}
#Around("someMethod()")
public Object aroundSomeMethod(ProceedingJoinPoint pjp) throws Throwable
{
// some code
}
}
Annotation:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface AnnotationName
{
public String someString();
public boolean someBoolean();
}
The use of the annotation:
#AnnotationName(
someString= "string",
someBoolean = false
)
private void mycode()
{//code }
Following aspect code would advice a target method annotated with #AnnotationName
#Component
#Aspect
public class SomeMethodAspect {
#Pointcut("#annotation(annotationName) && within(so.qn69016852..*)")
private void someMethod(AnnotationName annotationName) {}
#Around("someMethod(annotationName)")
public Object aroundSomeMethod(ProceedingJoinPoint pjp,AnnotationName annotationName) throws Throwable
{
System.out.println(annotationName.someString());
System.out.println(annotationName.someBoolean());
return pjp.proceed();
}
}
Couple of corrections/observations .
Spring AOP cannot advice a private method of a Spring bean. The mycode() method should be in a bean and ideally public. ( Refer )
The Aspect should also be a spring bean. This can be achieved by annotating the aspect with #Component
Remember to limit the scope : https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#writing-good-pointcuts
You may also go through this answer from #kriegaex to understand why an #annotation has a global scope.
Update :
The code shared by OP also works with modifying a typo ( a space between the AnnotationName and * in the pointcut expression ) . The observations shared earlier holds good here as well.
#Component
#Aspect
public class SomeMethodAspect {
#Pointcut("execution(#so.qn69016852.anno.AnnotationName * so.qn69016852..*.*(..))")
private void someMethod() {}
#Around("someMethod() && #annotation(annotationName)")
public Object aroundSomeMethod(ProceedingJoinPoint pjp,AnnotationName annotationName) throws Throwable
{
System.out.println(annotationName.someBoolean());
System.out.println(annotationName.someString());
return pjp.proceed();
}
}

How can I create a Pointcut or Around for extended classes with shared interface?

I have an abstract service class.
abstract class AbstractService<T> {
public void saveNew(T entity) {
}
}
And I have two more abstract classes extends AbstractService and implement a shared interface.
abstract class MoreAbstractService2<T extends Some2>
extends AbstractService<T>
implements SharedInterface {
}
abstract class MoreAbstractService3<T extends Some3>
extends AbstractService<T>
implements SharedInterface {
}
Now I want to validate the entity argument on these two extending services' saveNew(T) method.
How can I define a #Pointcut and (or) an #Around for following conditions?
extends the AbstractService class
implements the SharedInterface interface
you can use within as following:
within(com.somepackage.Super+)
where com.somepackage.Super is the fully qualified base class name and + means "all subclasses". Other pointcut is
execution(* com.somepackage.Super+.*(..))
R.G's solution has a few disadvantages:
The pointcut matches too many joinpoints.
Thus it needs reflection in order to filter out the unnecessary ones.
I am going to show you a stand-alone AspectJ solution (no Spring, I am not a Spring user), but the aspect would look just the same in Spring, you only need to make it a #Component or declare a #Bean factory in your configuration. But the same applies to all the classes you want to intercept, of course.
Because I prefer a full MCVE with all necessary dependency classes in order for you to be able to copy, compile and run it, and because I also added negative test cases (sub-classes only extending the abstract base class or only implementing the interface), this is a lot of code. So please bear with me:
Abstract classes, interface and helper classes:
package de.scrum_master.app;
public abstract class AbstractService<T> {
public void saveNew(T entity) {
System.out.println("Saving new entity " + entity);
}
}
package de.scrum_master.app;
public class Some2 {}
package de.scrum_master.app;
public class Some3 {}
package de.scrum_master.app;
public abstract class MoreAbstractService2<T extends Some2>
extends AbstractService<T>
implements SharedInterface {}
package de.scrum_master.app;
public abstract class MoreAbstractService3<T extends Some3>
extends AbstractService<T>
implements SharedInterface {}
package de.scrum_master.app;
public interface SharedInterface {
void doSomething();
}
Driver application (AspectJ + POJOs, not Spring):
This driver class contains some static inner classes subclassing the given base classes and/or implementing the shared interface. Two are used for positive tests (should be intercepted), two for negative tests (should not be intercepted). Each class also contains an additional method as another negative test case which should not be matched - better safe than sorry.
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
// Should be intercepted
InterceptMe1 interceptMe1 = new InterceptMe1();
interceptMe1.saveNew(new Some2());
interceptMe1.doSomething();
interceptMe1.additional();
printSeparator();
// Should be intercepted
InterceptMe2 interceptMe2 = new InterceptMe2();
interceptMe2.saveNew(new Some3());
interceptMe2.doSomething();
interceptMe2.additional();
printSeparator();
// Should NOT be intercepted
DontInterceptMe1 dontInterceptMe1 = new DontInterceptMe1();
dontInterceptMe1.saveNew(new Some2());
dontInterceptMe1.additional();
printSeparator();
// Should NOT be intercepted
DontInterceptMe2 dontInterceptMe2 = new DontInterceptMe2();
dontInterceptMe2.additional();
printSeparator();
}
private static void printSeparator() {
System.out.println("\n----------------------------------------\n");
}
static class InterceptMe1 extends MoreAbstractService2<Some2> {
#Override
public void doSomething() {
System.out.println("Doing something in MoreAbstractService2<Some2>");
}
public void additional() {
System.out.println("Additional method in MoreAbstractService2<Some2>");
}
}
static class InterceptMe2 extends MoreAbstractService3<Some3> {
#Override
public void doSomething() {
System.out.println("Doing something in MoreAbstractService3<Some3>");
}
public void additional() {
System.out.println("Additional method in MoreAbstractService3<Some3>");
}
}
static class DontInterceptMe1 extends AbstractService<Some2> {
public void additional() {
System.out.println("Additional method in AbstractService<Some2>");
}
}
static class DontInterceptMe2 implements SharedInterface {
#Override
public void doSomething() {
System.out.println("Doing something in SharedInterface"); }
public void additional() {
System.out.println("Additional method in SharedInterface");
}
}
}
Aspect:
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
#Aspect
public class EntityValidationAspect {
#Before(
"execution(* saveNew(*)) && " +
"args(entity) && " +
"target(de.scrum_master.app.SharedInterface) && " +
"target(de.scrum_master.app.AbstractService)"
)
public void validateEntity(JoinPoint thisJoinPoint, Object entity) {
System.out.println("-> Pre-save entity validation: " + entity);
}
}
As you can see, the aspect uses two target() pointcuts which must both match. It also specifically targets any saveNew method with a single argument saveNew(*), binding that argument as an advice method parameter via args().
For demonstration's sake I do not validate anything (I don't know how you want to do that) but just print the entity. Thus, a #Before advice is sufficient. If in case of negative validation you want to throw an exception, this advice type is also okay. If you need to do more, such as manipulate the entity's state or replace it before passing it on to the target method, call an alternative target method instead or none at all, return a specific result (in case of non-void methods, here not applicable), handle exceptions from the target method etc., you ought to use an #Around advice instead.
Console log:
-> Pre-save entity validation: de.scrum_master.app.Some2#28a418fc
Saving new entity de.scrum_master.app.Some2#28a418fc
Doing something in MoreAbstractService2<Some2>
Additional method in MoreAbstractService2<Some2>
----------------------------------------
-> Pre-save entity validation: de.scrum_master.app.Some3#5305068a
Saving new entity de.scrum_master.app.Some3#5305068a
Doing something in MoreAbstractService3<Some3>
Additional method in MoreAbstractService3<Some3>
----------------------------------------
Saving new entity de.scrum_master.app.Some2#1f32e575
Additional method in AbstractService<Some2>
----------------------------------------
Additional method in SharedInterface
----------------------------------------
Et voilĂ  - the aspect does exactly what you asked for, as far as I understand your requirement. :-) More specifically, it does not get triggered in the third case when saveNew is being called, but the class does not implement the interface.
Following code can be used for the validation mentioned.
The point cut is to intercept on the execution of a specific method for the subclasses of AbstractService and the code logic is to only validate if SharedInterface is a superinterface of the target bean.
The use of isAssignableFrom() is required as the interfaces proxied by the AOP proxy does not include SharedInterface. As per my understanding , a pointcut expression to match the second criteria will not be possible for the same reason and hence handled the requirement in the code logic.
Hope this helps
#Aspect
#Component
public class ValidationAspect {
#Pointcut("execution(* package.to.AbstractService+.saveNew(..))")
public void isAbstractServiceType() {
}
#Around("isAbstractServiceType() && args(entity) && target(bean)")
public void validateEntityArugments(ProceedingJoinPoint pjp, Object entity, Object bean) throws Throwable {
if (SharedInterface.class.isAssignableFrom(bean.getClass())) {
System.out.println(entity);
// ..validate
}
pjp.proceed();
}
}

spring mvc + spring aop + aspectj

I struggle to use aspect in Spring MVC project.
Method that is a pointcut is running fine, but without advise.
Here is class, that starts whole spring boot and that is root of spring context:
#Lazy
#SpringBootApplication
#EnableAspectJAutoProxy(proxyTargetClass=true)
#Configuration
public class MainSpringBootClass{
public static void main(String[] args)
{
SpringApplication.run(MainSpringBootClass.class, args);
}
}
Here is class with method, that is pointcut.
#Component
#Log
#Aspect
#EnableAspectJAutoProxy(proxyTargetClass=true)
public class MyExampleClass
{
public void example()
{
System.out.println("example");
}
}
And here is my aspect:
#Aspect
#Component
#EnableAspectJAutoProxy(proxyTargetClass=true)
public class MyAspect implements MethodBeforeAdvice
{
#Pointcut("execution(* com.example.MyExampleClass.example())")
public void asd()
{
// pointcut
}
#Before("asd()")
public void login()
{
System.out.println("im am logging in");
}
#Before("execution(* com.example.MyExampleClass.example())")
public void login2()
{
System.out.println("im am logging in2");
}
#Override
public void before(Method method, Object[] args, Object target) throws Throwable
{
System.out.println("aop before");
}
}
And here is my controller:
#RestController
#EnableAspectJAutoProxy(proxyTargetClass=true)
public class MyExampleController
{
private final MyExampleClass myExampleClass;
#Inject
public AdController(MyExampleClass myExampleClass)
{
this.myExampleClass = myExampleClass;
}
#RequestMapping("/")
public String index()
{
myExampleClass.example();
return "x";
}
}
As You can see, I have been trying to bruteforce correct result with annotations.
I have also seen on some website, that I need specific dependencies, so here are mine (pasting only those, related to aspects):
compile 'org.springframework:spring-aop:+'
compile 'org.aspectj:aspectjrt:+'
compile 'org.aspectj:aspectjweaver:+'
compile 'cglib:cglib:+'
compile 'cglib:cglib-nodep:+'
All dependencies have been successfully downloaded, project compiles and runs fine.
When I hit localhost:8080 then I see returned value "x", and inside logs I see "example".
However, I do not see any advices from spring aop nor aspectj - what am I doing wrong?
I am just using this project as sandbox to learn aspects, so I would be eager to learn, how to do it with each of Spring AOP and AspectJ.
The most important thing for me is to do it all without XML.
EDIT:
I have added simple constructor to MyAspect with println to check, if it is created (as it is normal spring bean with #Component after all) and it does - it is correctly created by spring.
EDIT 2:
IntelliJ IDEA tells me about methods login and login2: "This advice advices no method", but at the same time, I am able to jump (with control-click) from string, that is value in annotations to correct implementations.
All you should need is something like this:
#Aspect
#Component
public class MyAspect {
#Before("execution(* com.example.MyExampleClass.example(..))")
public void logBefore(JoinPoint pjp) throws Throwable {
System.out.println("before...");
}
}
You might have to replace all of the aspectJ dependencies with spring-boot-starter-aop .
Here's an example project that works (see RestControllerAspect.java):
https://github.com/khoubyari/spring-boot-rest-example

Spring AspectJ, pointcut before method execution where method OR class is annotated

I'm trying to get the value of an annotation via Spring Aop AspectJ-style, where the annotation can be on the class OR the method. I tried a lot of different things, but I can only get it to work when the annotation is on the method. I'd really like to annotate ONCE on the class - but advice all the methods of the class - and access the value of the class annotation in the advice. Here's where I've ended up:
Annotation:
#Inherited
#Target({ElementType.TYPE, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
public #interface MyAnnotation {
String value() default "";
}
Aspect:
#Aspect
public class MyAspect {
#Pointcut("execution(#com.myco.MyAnnotation * com.myco.somepackage..*.*(..))")
public void atExecution() { }
#Before("atExecution() && #annotation(myAnnotation)")
public void myAdvice(JoinPoint joinPoint, MyAnnotation myAnnotation) {
...
}
}
Any thoughts? Thanks.
Short answer
While you can formulate a pointcut that will match both directly annotated methods and methods of annotated types at the same time, you cannot make a pointcut and/or advice where you bind the value of the annotation (i.e. use the annotation value in the advice code).
#Aspect
public class MyAspect {
#Pointcut("execution(#com.myco.MyAnnotation * com.myco.somepackage..*.*(..))")
public void atExecutionOfAnnotatedMethod() {}
#Pointcut("execution(* (#com.myco.MyAnnotation com.myco.somepackage..*).*(..))")
public void atExecutionOfMethodsOfAnnotatedClass() {}
#Before("atExecutionOfAnnotatedMethod() && #annotation(myAnnotation)")
public void myAdviceForMethodAnnotation(JoinPoint joinPoint, MyAnnotation myAnnotation) {
System.out.println("myAdviceForMethodAnnotation: " + myAnnotation.value());
}
#Before("atExecutionOfMethodsOfAnnotatedClass() && #this(myAnnotation)")
public void myAdviceForTypeAnnotation(JoinPoint joinPoint, MyAnnotation myAnnotation) {
System.out.println("myAdviceForTypeAnnotation: " + myAnnotation.value());
}
// /* the following pointcut will result in "inconsistent binding" errors */
// #Pointcut("(atExecutionOfAnnotatedMethod() && #annotation(myMethodAnnotation)) || (atExecutionOfMethodsOfAnnotatedClass() && #this(myTypeAnnotation))")
// public void combinedPointcut(MyAnnotation myMethodAnnotation, MyAnnotation myTypeAnnotation) {}
}
Some detail
To combine the two separate pointcuts (atExecutionOfAnnotatedMethod and atExecutionOfMethodsOfAnnotatedClass) we would have to use the OR (||) construct. Since the OR construct doesn't guarantee that either of the two annotation bindings will be present at advice execution, they will both result in a compile error (inconsistent binding).
You can still handle both cases in separate advices, you may also delegate the actual advice code to a common method to avoid duplication. In that case you'll need to take care of the case where both the type and the method is annotated with #MyAnnotation because that would match both pointcuts and would result in your method doubly advised by both advices, hence your common advice handling code will execute twice.
Combining the two
If you need to combine the two cases while defending against doubly advising the target code, you need to set up a precedence between the method level annotation and the class level annotation. Based on the principle of specificity, I'd suggest to go on the route where the method level annotation takes precedence over the class level one. Your aspect would look like this:
#Aspect
public class MyCombinedAspect {
#Pointcut("execution(#com.myco.MyAnnotation * com.myco.somepackage..*.*(..))")
public void atExecutionOfAnnotatedMethod() {}
#Pointcut("execution(* (#com.myco.MyAnnotation com.myco.somepackage..*).*(..))")
public void atExecutionOfMethodsOfAnnotatedClass() {}
#Before("atExecutionOfAnnotatedMethod() && !atExecutionOfMethodsOfAnnotatedClass() && #annotation(myAnnotation)")
public void myAdviceForMethodAnnotation(JoinPoint joinPoint, MyAnnotation myAnnotation) {
handleBeforeExecution(joinPoint, myAnnotation);
}
#Before("atExecutionOfMethodsOfAnnotatedClass() && !atExecutionOfAnnotatedMethod() && #this(myAnnotation)")
public void myAdviceForTypeAnnotation(JoinPoint joinPoint, MyAnnotation myAnnotation) {
handleBeforeExecution(joinPoint, myAnnotation);
}
#Before("atExecutionOfMethodsOfAnnotatedClass() && atExecutionOfAnnotatedMethod() && #annotation(myMethodAnnotation)")
public void myAdviceForDoublyAnnotated(JoinPoint joinPoint, MyAnnotation myMethodAnnotation) {
handleBeforeExecution(joinPoint, myMethodAnnotation);
}
protected void handleBeforeExecution(JoinPoint joinPoint, MyAnnotation myAnnotation) {
System.out.println(myAnnotation.value());
}

Spring Aspects (AspectJ) does not seem to work

Well I really don't know why this does not work:
Every jar needed is in the place. Including aspectjrt.
Basically I start with configuration class:
#Configuration
#ComponentScan(basePackages = { "some.path" })
#EnableAspectJAutoProxy
public class SomeConf { ... }
Then I have my Aspect:
#Component
#Aspect
public class ControllerLoggerAspect {
#Pointcut("execution(* some.path.ATest.*(..))")
private void aspectTest() {
System.out.println("\n\n ASPECT WORKING \n\n");
}
}
Under some.path I have Atest class:
package some.path;
public class ATest {
public void dummyMethod(){
System.out.println("\n\n\nDummy static executed\n\n\n");
}
}
Imagine now that I have controller:
#Controller
#RequestMapping(value = "/mapping")
public class SomeController {
#RequestMapping(value = "/something")
public ResponseEntity<String> publish(#RequestParam("Id") Long[] ids) {
//aspect should be invoked here
new ATest().dummyMethod();
return new ResponseEntity<>("{ \"status\": \"stubbed\"}", HttpStatus.OK);
}
}
Everything is invoked properly except aspect method. No errors, no exceptions, nothing. Any ideas?
Spring AOP only works on Spring beans, i.e. if you want to intercept one of its methods, class ATest needs to be a #Component.
You need to configure the advice you want execute. The pointcut only helps in determining the join points. The advice will be executed, not the pointcut.
You could write a Advice like this using your pointcut "aspectTest()":
#Before("aspectTest()")
public void beforeAdvice(){
System.out.println("\n\n ASPECT WORKING \n\n");
}
Or you could just replace your Pointcut annotation in the example with an advice annotation like this:
#Before("execution(* some.path.ATest.*(..))")
public void aspectTest() {
System.out.println("\n\n ASPECT WORKING \n\n");
}
Here you have a list of all adivces:
#Before("aspectTest()")
#After("aspectTest()")
#AfterReturning(pointcut = "aspectTest()", returning="retVal")
#AfterThrowing(pointcut = "aspectTest()", throwing="ex")
#Around("aspectTest()")

Resources