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();
}
}
Related
I just tried an #Around for a method defined with my repository interface.
#Repository
public interface MyRepository {
Page<Some> getAllSome();
}
With my Aspect I can't specify the getAllSome method.
#Aspect
#Component
public class MyRepositoryAspect {
#Around("execution(* my.package.MyRepository.*(..))") // works
//#Around("execution(* my.package.MyRepository.getAllSome(..))") // does't work
public Object aroundGetAllSome(ProceedingJoinPoint joinPoint) throws Throwable {
}
}
Why the version with .getAllSome(..) doesn't work? How can I match a specific method?
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();
// ...
}
I have the following interface:
public interface Performance {
public void perform();
}
Implemented by the following class:
#Component
public class Woodstock implements Performance {
public void perform() {
System.out.println("Woodstock.perform()");
}
}
The aspect I want to apply is this one:
#Aspect
public class Audience {
#Pointcut("execution(* concert.Performance.perform(..)) "
+ "&& bean('woodstock')"
)
public void performance() {}
#Around("performance()")
public void watchPerformance(ProceedingJoinPoint jp) {
try {
System.out.println("Taking seats");
jp.proceed();
System.out.println("CLAP CLAP CLAP!!!");
} catch (Throwable e) {
System.out.println("Demanding a refund");
}
}
}
The configuration file for the program is declaring the following beans:
#Bean
public Audience audience(){
return new Audience();
}
#Bean
public Performance woodstock(){
return new Woodstock();
}
And the test I'm running is this:
#Autowired
ApplicationContext context;
#Autowired
#Qualifier("woodstock")
Performance performance;
#Test
public void test(){
printBeanNames();
performance.perform();
}
Now, in the Audience class I specified in the annotation that I want to apply the aspect for the perform() method from the interface concert.Performance (execution(* concert.Performance.perform(..))) and I am explicitly limiting the application of the aspect to the bean whose name is "woodstock" (&& bean('woodstock')).
But when I run it I see that the aspect is not applied to the woodstock bean, although that bean is the one being used (it prints Woodstock.perform())
If instead of bean() I use !bean() then it does work, so it seems that it cannot consider that bean as the one named "woodstock".
Is there something wrong in the code above?
The special Spring AOP bean() pointcut works like bean(myBean) or bean(*Bean), there are no inline single or double quotes around the bean name according to the Spring AOP documentation (scroll down a little bit in this chapter).
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());
}
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()")