What's the meaning of the method use #Pointcut in Spring AOP,just a pointcut signature? - spring

learning the Spring AOP, the code like this:
#Component
#Aspect
public class FanAnnotationImpl {
#Pointcut("#annotation(com.fan.spboot.core.aopdemo.FanAnnotation)")
private void entry(){
System.out.println("entry annotation");
}
#Around("entry()")
public void around(ProceedingJoinPoint joinPoint)throws Throwable{
System.out.println("around before");
try {
joinPoint.proceed();
}catch (Exception e){
e.printStackTrace();
}
System.out.println("around after");
}
#Before("entry()")
public void before(){
System.out.println("Before entry");
}
#After("entry()")
public void after(){
System.out.println("After entry");
}
}
the spring-aop-pointcut-tutorial has a introduction:
"The method declaration is called the pointcut signature. It provides a name that can be used by advice annotations to refer to that pointcut."
What make me feel puzzled is the method use #Pointcut ,it's just a pointcut signature?
Because I find the code in this method not executed, and change the type of this method is OK;
Then why is a method? Use a variable is also OK?

You already quoted the manual yourself. It is pretty clear, is it not? Spring AOP is based on annotations, not on variables. Annotations are a standard way in Java to add information to classes, methods or other language elements.
A #Pointcut method is only a way to define pointcuts which can be used in multiple places, e.g. if you want to combine multiple pointcuts like pointcut1 && (pointcut2 || pointcut3) or just use the same pointcut in multiple advice methods. It is a way for you as a developer not to have to repeat yourself and write the same pointcut many times. Another advantage is that you can modify the pointcut in one place and it gets updated everywhere it is used.
The method annotated by #Pointcut of course is never called by Spring AOP because the method is only a dummy you need to get decorated by the pointcut annotation. You need to put the annotation somewhere, after all.
If you use your pointcut in only a single place, there is no need to define it via #Pointcut, you can just write the pointcut directly into your #Before, #After or #Around annotation.
Actually this answer is pretty much superfluous because everything is explained well in the Spring AOP manual.

Related

enabling aspectj with environment variables

How can we enable/disable an aspect using environment variables?
I know it is possible to enable/disable aspectj in spring boot application using following properties
spring:
aop:
auto: true
Or:
spring.aop.auto=true
And removing #EnableAspectJAutoProxy, but this stops all of our other aspects / joins.
This is the one I want to disable, how do I do it
#Aspect
#Component
public class SomeAspect {
#Around("#annotation(someAnnotation)")
public Object doSomething(ProceedingJoinPoint joinPoint, SomeAnnotation sa) throws Throwable {
// ...
}
//others
}
In order to dynamically deactivate a single advice inside an aspect class, you can use an if() pointcut.
If you want to completely disable an aspect (or any other Spring bean or component) based on conditions like e.g. a property in application.config, have a look at #Conditional and its special cases #ConditionalOn*. For example:
#Aspect
#Component
#ConditionalOnProperty(prefix = "org.acme.myapp", name = "aspect_active")
public class SomeAspect {
// ...
}
Something like this in application.config would deactivate the aspect:
org.acme.myapp.aspect_active=false
The aspect would also be inactive if there was no such property in the application config at all. If you rather want to default to an active aspect, just use
#ConditionalOnProperty(prefix = "org.acme.myapp", name = "aspect_active", matchIfMissing = true)
You can further fine-tune the behaviour as described in the javadoc.
See also:
https://www.baeldung.com/spring-conditional-annotations
https://www.baeldung.com/spring-conditionalonproperty
Update:
In order to dynamically deactivate a single advice inside an aspect class, you can use an if() pointcut.
Oops, sorry, I am a native AspectJ user and forgot that Spring AOP does not support the if() pointcut designator. So probably the best you can do is an if expression at the beginning of your advice, depending on a #Value property.
#Value("${org.acme.myapp.advice_active:false}")
private boolean adviceActive;
#Around("#annotation(someAnnotation)")
public Object doSomething(ProceedingJoinPoint joinPoint, SomeAnnotation sa) throws Throwable {
// Skip advice logic if inactive, simply proceed and return original result
if (!adviceActive)
return joinPoint.proceed();
// Regular advice logic if active
System.out.println(joinPoint);
// Either also proceed or do whatever else is the custom advice logic, e.g.
// - manipulate method arguments,
// - manipulate original return value,
// - skip proceeding to the original method altogether and return something else.
return joinPoint.proceed();
}
Of course, you can also use my original solution and just factor out the advice you wish to deactivate into a separate aspect class, if you need that kind of granularity. That would be less hassle, and the advice method code would be more readable.

Spring AOP pointcut expression for Subclass only method

I have a scenario in which i need to intercept some subclass methods, but i couldn't find a proper pointcut expression to do so.
I have a client facing interface InfoService which has a method getClientDetails.
package sample;
public interface InfoService {
InfoVO getClientDetails(int id);
}
The implementation class has some nested methods like get*Info().
package sample;
public class InfoServiceImpl implements InfoService{
public InfoVO getClientDetails(int id) {
InfoVO clientInfo = new InfoVO();
clientInfo.setA(getAInfo(id));
clientInfo.setB(getBInfo(id));
clientInfo.setC(getCInfo(id));
return clientInfo;
}
public Object getAInfo(int id) {
return null;
}
public Object getBInfo(int id) {
return null;
}
public Object getCInfo(int id) {
return null;
}
}
When the user invoke the getClientDetails method i would like to intercept the get*Info() methods. I can easily intercept the getClientDetails but nothing seems to intercept the subclass method. I even tried annotating those info methods using custom annotation but with no luck. So far I came up with the following aspect
<aop:aspect ref="infoAspect">
<aop:pointcut expression="execution(* sample.InfoService+.*Info(..))"
id="infoMethods" />
<aop:around method="aroundAdviceForinfoMethods" pointcut-ref="infoMethods" />
</aop:aspect>
Setting the <aop:aspectj-autoproxy proxy-target-class="false"/> to true or false didn't help either.
I know AOP cannot intercept on private subclass methods but these are public ones. Is it even possible to do this? Any help is much appreciated.
PS: The example shown is for demo purpose. The actual implementations are huge, so moving those inner methods to a different bean and invoking won't be feasible.
This is a classic one, having been asked here dozens of times already. I guess you have not read the Spring manual's info about Spring AOP being proxy-based and that for this reason Spring aspects cannot intercept self-invocation. If you really need AOP to work for self-invoked methods (even private ones if necessary), say goodbye to Spring AOP and hello to full AspectJ and LTW (load-time weaving).

How to write a pointcut to get an operation on a class method?

I have a class named PersonDAOImpl, where I use Hibernate to persist data on my data base.
In order to audit the class, I created another class using the #Aspect annotation, where I declared some Pointcuts and Advices. Everytime I insert, update or delete a person, I'm able to store on a table proper information about the operation executed.
However now I have more DAO classes, and I don't want to create more Aspects classes or more Advices for every method on every DAO. Instead, I want to "capture" the operations of persist(), update() or delete() in a single pointcut.
For example I have this method on my DAO:
#Override
#Transactional
public void addPerson(Person p) {
Session session = this.sessionFactory.getCurrentSession();
session.persist(p);
session.flush();
logger.info("Person saved successfully, Person Details="+p);
}
And this is how I audit the insertions:
#Pointcut("execution(* com.dacasals.raspertwo.dao.PersonDAOImpl.addPerson(..))")
public void addNewPerson() {
}
#After("addNewPerson()")
public void aP() {
System.out.println("added a new person");
operation = "Insert";
date = Calendar.getInstance().getTime();
register(operation, date);
}
But I want something like #Pointcut("execution(*com.dacasals.raspertwo.dao.anydaoclass.anymethod.persist(..))")
I mean a pointcut for any method on any of my DAO classes where persist() is called. Same thing for update() and delete().
How can I achieve this? I've tried with 'within' and 'withincode' but nothing happens. Thanks in advance.
Instead of execution expression you might want to use the within expression something like this.
within(com.yourpackage..*)
For this you will have to make sure that all your DAO's are within this package. The above expression will match all the methods defined in classes inside package com.yourpackage and classes sub-packages.
Here is a short doc if you would like to configure it some other way,
Pointcut Expressions in Spring
I would use the 'AroundAdvice'.
Quote for documentation:
Around advice runs "around" a matched method execution. It has the opportunity to do work both before and after the method executes, and to determine when, how, and even if, the method actually gets to execute at all.
Around Advice Spring documentation
So, in your example, you would have a method:
#Around("com.dacasals.raspertwo.dao.*.persist(..)")
public Object doSomething(ProceedingJoinPoint pjp) throws Throwable {
}
This way every time someone on 'com.dacasals.raspertwo.dao' calls persist method, Spring Aspect will invoke doSomething method. Around advice works both before and after the method executes, so you should take care to don't do the same thing twice.
I would create a specific method for every operation (one for insert, another for update and delete) but you could use the same method using short-circuit operators. That way, your #Around annotation would be like:
#Around("com.dacasals.raspertwo.dao.*.persist(..) || com.dacasals.raspertwo.dao.*.update(..) || com.dacasals.raspertwo.dao.*.delete(..)")
public Object doSomething(ProceedingJoinPoint pjp) throws Throwable {
}

Using Aspect to annotate methods with #InsightOperation for Spring Insight

I wanted to instrument a large number of classes to use with Spring Insight and instead of adding the #InsightOperation manually to the methods, I wrote an aspect to annotate the methods using point cuts.
However, this is not working. While the manual annotation affects the Spring Insight trace logging, the AspectJ method does not work.
Is there anything I am doing wrong here? (I decompiled the classes after aspectizing and do find the annotation in the class methods)
This is the aspect code snippet:
declare #method :public * com.example.IExample.execute(..) : #InsightOperation;
Spring documentation says this:
Use of the #Insight* annotations are
optional. They make it easy for end
users to define custom operation
frames and end points without needing
to create a plug-in. Because end user
code modification is required to use
the annotations, they are an option
for users who cannot or do not wish to
write aspects.
http://static.springsource.com/projects/tc-server/2.5/devedition/htmlsingle/devedition.html
So looks like the only way is to write a custom plugin
http://static.springsource.com/projects/tc-server/2.5/devedition/htmlsingle/devedition.html#tutorial-plugin
It is possible that the Insight LTW does not pick up your introduced annotations. I'll have to dig deeper on that.
In the meantime, you can try a more low-level annotation:
com.springsource.insight.collection.method.MethodOperationsCollected
If you look at the spring-core plugin, you will see that it does something similar:
public aspect RepositoryMethodOperationCollectionAspect {
declare #type: #Repository * : #MethodOperationsCollected;
}
An easy work around is to call another method from within your aspect method to continue executing the join point. I only tried calling a static method in a static class. See below my code for adding the #InsightOperation to all my JSON serialization.
My aspect:
#Aspect
public class JSONSerializerAspect {
#Around("call(* *.JSONSerializer.serialize(..)) && args(target)")
public Object serialize(ProceedingJoinPoint joinPoint, Object target) throws Throwable {
return JSONSerializationWrapper.serialize(joinPoint, target);
}
}
The static class it is calling:
public class JSONSerializationWrapper {
#InsightOperation(label = "JSON_SERIALIZATION")
public static Object serialize(ProceedingJoinPoint joinPoint, Object target) throws Throwable {
return joinPoint.proceed(new Object[]{target});
}
}
I'm using this myself and tested that it works.

Need help creating a specific pointcut inside of a method

I started with an original question on
Need help creating a specific pointcut that utilizes a value from a method annotation
I decided I wanted to ask another question to change the approach I was taking.
I have a method (navigation), that has a call inside of that method to another method which I would like to have #Around advice.
#RequestMapping(method = RequestMethod.GET)
public String navigation(ModelMap model) {
...
// Call Auto Handling
logger.info("Call AutoHandling");
this.processAutoHandling(callSession, FunctionalArea.PRE_MAIN_MENU);
}
...
return forward(returnView);
}
Is this possible as I cannot seem to get this to work if the method is inside of the same class.
This work if it was not on the object itself:
#Around("execution(* *.processAutoHandling(..)) &&" +
"args(callSession, functionalArea) && " +
"args(functionalArea) && " +
"target(bean)"
)
public Object processAutoHandlingCall2(ProceedingJoinPoint jp,
CallSession callSession,
FunctionalArea functionalArea,
Object bean)
throws Throwable {
logger.debug("processAutoHandleCall");
return jp.proceed();
}
With this call in my controller:
autoHandlingComponent.processAutoHandling(callSession, FunctionalArea.PRE_MAIN_MENU);
instead of
this.processAutoHandling(callSession, FunctionalArea.PRE_MAIN_MENU);
It seems that you are using Spring's proxy-based AOP. If so, this is a known limitation. See Understanding AOP Proxies from Spring documentation for more details. You have two ways to solve this issue:
Use the AopContext.currentProxy() approach outlined in the documentation. I will discourage this approach, since your code will now be tied to Spring AOP quite explicitly.
Use AspectJ's byte-code weaving. Since there is no proxy involved with it, you won't have the issue with 'this' pointing to original object and proxy is transparently available only to external objects.

Resources