Using Aspect to annotate methods with #InsightOperation for Spring Insight - spring

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.

Related

Why doesn't Spring support the order of annotations advice at the method level?

#Component
#Aspect
#Slf4j(topic = "e")
public class NotVeryUsefulAspect{
#Pointcut("within(com.lc.aop.for_source.service.impl.AAopServiceImpl)")
public void pointCutWithinAAopService(){
}
#Pointcut("#within(com.lc.aop.for_source.service.XAnnotation)")
public void pointCutAnnotation(){
}
#Before("pointCutWithinAAopService()")
#Order(0)
public void adviceBeforeAAopService(){
log.debug("=======before aop service========");
}
#Before("pointCutAnnotation()")
#Order(-1)
public void adviceBeforeAAopService2(){
log.debug("=======before aop annotation========");
}
}
#Slf4j(topic = "e")
#Component("a")
#XAnnotation
public class AAopServiceImpl implements AopService {
#Override
public void m() {
log.debug("a -AAopServiceImpl");
}
}
Based on the advice-ordering
Consider collapsing such advice methods into one advice method per join point in each #Aspect class or refactor the pieces of advice into separate #Aspect classes that you can order at the aspect level via Ordered or #Order.
Do I understand correctly that the #Order does not work in this case? Why not suport the method level order?
I think this is a very simple function, but it can avoid some unnecessary misunderstandings about #Order
I would like to order advice by method level.
Well, the answer to your question is in the sentence directly before the one you quoted, in the very same paragraph of the very same info box:
When two pieces of the same type of advice (for example, two #After advice methods) defined in the same #Aspect class both need to run at the same join point, the ordering is undefined (since there is no way to retrieve the source code declaration order through reflection for javac-compiled classes).
That question should probably be posed to the chaps working on the springframework, that project is located at: https://github.com/spring-projects/spring-framework.
What you're asking for makes sense, but keep in mind that Order is meant to prioritize the loading of beans from the context, so it makes sense that Order needs to be applied to the Aspect and not the Pointcut itself.

Interceptor on a class method belonging to External Dependency

We have an Aspect in our code that had been PointCut on Hibernate class.
Our Aspect class looks something like this:
#PointCut("(execution(* *.getQueryString(..))" + "|| execution(* *.getQuery(..)))" + "&& (target(org.hibernate.engine.NamedSQLQueryDefinition))")
public void aroundNamedSQLQueryDefinitionGetQuery() {
}
#Around("aroundNamedSQLQueryDefinitionGetQuery")
public String addExtraFilter(ProceedingJoinPoint pjp) throws Exception {
//Logic to add extra filter to the Query.
}
Now we are trying to migrate this code to Quarkus. We have replaced Aspects with Interceptors which were present on code belonging to our modules.
But how do we add Interceptors on Hibernate classes?
Is there an alternate way to achieve this?
A Quarkus extension would allow you to manipulate the Hibernate classes (or your own classes).
To scaffold a basic extension,
mvn io.quarkus.platform:quarkus-maven-plugin:2.10.1.Final:create-extension -N \
-DgroupId=org.you \
-DextensionId=aspectorama
Then, in the [whatever]Processor class that gets created, you could add an AnnotationTransformerBuildItem
#BuildStep
AnnotationsTransformerBuildItem transform() {
return new AnnotationsTransformerBuildItem(new AnnotationsTransformer() {
public boolean appliesTo(org.jboss.jandex.AnnotationTarget.Kind kind) {
return kind == org.jboss.jandex.AnnotationTarget.Kind.METHOD;
}
public void transform(TransformationContext context) {
if ("org.hibernate.engine.NamedSQLQueryDefinition".equals(context.getTarget().asMethod().declaringClass().name()) && ("getQueryString").equals(context.getTarget().asMethod().name()) {
context.transform().add(YourAnnotation.class).done();
}
}
});
}
(I haven't tested that, and I may not have quite the method names you intended. I only did getQueryString not getQuery... but it shows the idea.)
You may also need to tell Quarkus about your interceptor if it's not in the main application codebase:
/**
* Makes the interceptor as a bean so we can access it.
*/
#BuildStep
void beans(BuildProducer<AdditionalBeanBuildItem> producer) {
producer.produce(AdditionalBeanBuildItem.unremovableOf(YourInterceptor.class));
producer.produce(AdditionalBeanBuildItem.unremovableOf(OtherExtraBean.class));
}
It may be that the annotation route isn't the best for your use case, and you could make the changes you needed more directly. It's worth browsing all the Quarkus build items, which are kind of like a library of built-in extension capabilities. For example, you can use #Record to create bytecode.

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.

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

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.

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