I happen to have an #Aspect that declares a method that is intercepted by a pointcut of another aspect. The aspects are created with compile-time weaving and the container is instantiated using Spring.
I annotated my aspect with #Configurable to tell Spring that the component is being created outside the container. I happen to have a static reference to a Log object in this aspect too. To summarize, the code looks something like this
#Aspect
#Configurable
public class MyAspect {
private static final Log log = LogFactory.getClass(MyAspect.class);
// Autowired dependencies
// Pointcuts and advice
// Happens to be a pointcut of some other Advice
#Asynchronous
private Object someMethod(...) {
}
}
During AspectJ compilation, I do not see the message I expect, which looks something like this:
weaveinfo Join point 'method-call(java.lang.Object mypackage.someMethod(...))' in Type 'mypackage.MyAspect' (MyAspect.java:30) advised by around advice from 'anotherpackage.AsynchronousAspect' (from AsynchronousAspect.java))
As expected, the third-party advice is never invoked at this point. However, if I add a simple log entry to my advice, something like
log.debug("Join point invoked!");
Then the compilation happens correctly and all the aspects are wired (including my third party dependencies) and invoked correctly.
What does adding a log entry do to change my assumptions?
What you want to do is pretty straightforward and not dangerous at all if you know what you are doing. Please apologise that I am not a Spring user and that I prefer native AspectJ syntax to #AspectJ. This little sample runs just fine:
public class Application {
public static void main(String[] args) {
System.out.println("Hello world!");
someMethod();
}
private static void someMethod() {
System.out.println("Doing something ...");
}
}
public aspect FirstAspect {
void around() : execution(void *..main(..)) {
System.out.println(thisJoinPointStaticPart + ": " + someMethod("before", "main"));
proceed();
System.out.println(thisJoinPointStaticPart + ": " + someMethod("after", "main"));
}
private Object someMethod(String position, String methodName) {
return position + " " + methodName;
}
}
public aspect SecondAspect {
Object around() : execution(* *..someMethod(..)) {
System.out.println(thisJoinPointStaticPart + ": before someMethod");
Object result = proceed();
System.out.println(thisJoinPointStaticPart + ": after someMethod");
return result;
}
}
The result is as expected:
execution(Object FirstAspect.someMethod(String, String)): before someMethod
execution(Object FirstAspect.someMethod(String, String)): after someMethod
execution(void Application.main(String[])): before main
Hello world!
execution(void Application.someMethod()): before someMethod
Doing something ...
execution(void Application.someMethod()): after someMethod
execution(Object FirstAspect.someMethod(String, String)): before someMethod
execution(Object FirstAspect.someMethod(String, String)): after someMethod
execution(void Application.main(String[])): after main
If furthermore you are concerned with thhe order in which aspects are applied/executed, please use declare precedence.
If you experience problems with accessing e.g. private members, you need to use a privileged aspect.
Update: Changed usage of thisEnclosingJoinPointStaticPart to thisJoinPointStaticPart. That was just a copy & paste error. The result is the same on execution join points, but anyway the correction shows better the code's intent.
Related
I am trying typecast a lambda argument to a class type, however I am always getting classcast exception. Here is the use case I am trying to achieve.
class A has a method public void foo(Supplier<?> msg)
I put an Apsect on the foo() method to capture the argument(In this case actual value of msg)
class B is invoking foo() method using an instance of class A using lambda expression
class B{
public void bar(){
A a=new A()
a.foo(()->{ new MyCustomObject()});
}
}
At runtime in my AOP class I am always getting argurment type of foo() as B$$lambda$0/13qdwqd
Question How do i get the actual class type of method Supplier argument(In this case MyCustomObject?
Aspect code
Class MyAspect{
#Around("call(public void com.mypkg.A.foo(..))")
public Object captureLambdaType(final ProceedingJoinPoint pjp) throws Throwable {
System.out.println("lambda called: [" + pjp.getSignature() + "] "+
"with parameter [" + pjp.getArgs()[0] + "]");
Supplier<MyCustomObject> obj= (Supplier<MyCustomObject> )(pjp.getArgs()[0]);
}
}
Thanks in advance!
Why are you making a simple thing complicated? User JB Nizet already told you:
The only way to get the type of the object returned by the supplier is to call the supplier.
Your own code does that in a very contrived way (after I fixed it to make it compile because it is buggy), using reflection. Just use AspectJ argument parameter binding via args() and make it type-safe. Also use #Before instead of #Around if you just want to log the Supplier's return value and not otherwise influence method execution. This way you can avoid calling ProceedingJoinPoint.proceed(), something necessary but completely missing in your "solution" sample code.
How about this little MCVE?
package de.scrum_master.app;
public class MyCustomClass {}
package de.scrum_master.app;
import java.util.function.Supplier;
public class A {
public void foo(Supplier<?> msg) {}
}
package de.scrum_master.app;
public class B {
public void bar() {
A a = new A();
a.foo(() -> new MyCustomClass());
}
public static void main(String[] args) {
new B().bar();
}
}
package de.scrum_master.aspect;
import java.util.function.Supplier;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
#Aspect
public class MyAspect {
#Before("execution(* *(*)) && args(supplier)")
public void methodCallWithSupplierArgument(JoinPoint thisJoinPoint, Supplier<?> supplier) throws Exception {
System.out.println(thisJoinPoint + " -> " + supplier.get());
}
}
The console log when running B.main(..):
execution(void de.scrum_master.app.A.foo(Supplier)) -> de.scrum_master.app.MyCustomClass#66a29884
This is the same as your aspect is trying to do, just more cleanly. I think it definitely is more readable, too.
Caveat: Please think twice before calling get() on the supplier if the supplier has a side effect or is expensive to calculate. I know that pure functions (i.e. code implementing functional interfaces in Java speak) should never have any side effects, but if coded in a bad style they easily can. So be careful.
Update: Talking about the caveat with the side effect, let me show you something. Just extend the application code a little bit so as to actually evaluate the supplier and (optionally) return its result:
package de.scrum_master.app;
import java.util.function.Supplier;
public class A {
public Object foo(Supplier<?> msg) {
return msg.get();
}
}
And now also let us extend the aspect to actually trigger logging whenever a supplier's get() method is called:
package de.scrum_master.aspect;
import java.util.function.Supplier;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
#Aspect
public class MyAspect {
#Before("execution(* *(*)) && args(supplier)")
public void methodCallWithSupplierArgument(JoinPoint thisJoinPoint, Supplier<?> supplier) throws Exception {
System.out.println(thisJoinPoint + " -> " + supplier.get());
}
#AfterReturning(pointcut = "call(public * java.util.function.Supplier+.get())", returning = "result")
public void supplierEvaluated(JoinPoint thisJoinPoint, Object result) throws Exception {
System.out.println(thisJoinPoint + " -> " + result);
}
}
Now the console log will be:
call(Object java.util.function.Supplier.get()) -> de.scrum_master.app.MyCustomClass#66a29884
execution(Object de.scrum_master.app.A.foo(Supplier)) -> de.scrum_master.app.MyCustomClass#66a29884
call(Object java.util.function.Supplier.get()) -> de.scrum_master.app.MyCustomClass#4769b07b
Can you see how Supplier.get() is called twice, returning two different MyCustomClass objects, namely MyCustomClass#66a29884 and MyCustomClass#4769b07b? This is because both the application and the first aspect advice call get(). The latter does not really log the same object as the one created by the application, so even without further side effects you are logging the wrong thing and executing a supplier method twice instead of just once.
So let us clean this up by not calling get() anymore from the first advice method:
#Before("execution(* *(*)) && args(supplier)")
public void methodCallWithSupplierArgument(JoinPoint thisJoinPoint, Supplier<?> supplier) throws Exception {
System.out.println(thisJoinPoint + " -> " + supplier); // no 'get()' call anymore
}
Now the log becomes clean:
execution(Object de.scrum_master.app.A.foo(Supplier)) -> de.scrum_master.app.B$$Lambda$1/1349393271#66a29884
call(Object java.util.function.Supplier.get()) -> de.scrum_master.app.MyCustomClass#4769b07b
Another advantage is that now the get() result gets logged whenever the method is really called (could be done synchronously, asynchronously, multiple times or never) and not when the aspect executes it redundantly.
P.S.: If you are wondering why I am so meticulous about not executing get() just for logging purposes, just imagine that the lambda opens a database connection, creates a 4 GB file, downloads a 4K video with duration 90 minutes or whatever. It would be done twice, just so as to let you log it.
Thanks you everyone for your comments. I was able to solve it by invoking get() method on the lambda. Sample code give below
Class MyAspect{
#Around("call(public void com.mypkg.A.foo(..))")
public Object captureLambdaType(final ProceedingJoinPoint pjp) throws Throwable {
System.out.println("lambda called: [" + pjp.getSignature() + "] "+
"with parameter [" + pjp.getArgs()[0] + "]");
//Get the lambda argument
Object arg=pjp.getArgs()[0];
//Get the argument class type
Class clazz=arg.getClass();
for (Method method : clazz.getDeclaredMethods()) {
method.setAccessible(true);
Object obj=method.invoke(obj,null);
if(obj instanceof MyCustomClass){
MyCustomClass myObject= (MyCustomClass) obj;
System.out.println("Hurray");
}
}
}
}
I am kinda new to Aspects and I am trying to surround a function with an aspect but I can't get it called.
The method signature is as follows:
public <T> T get(String uri, List<BasicNameValuePair> nameValuePairs, final Class<T> clazz)
and it's defined in class with fully-qualified name:
com.X.Y.infrastructure.rest.RestClient
And the Aspect
#Aspect
public class WebRequestTimeLoggerAspect {
#Around("execution(* com.X.Y.infrastructure.rest.RestClient.get(..))")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("hijacked method : " + joinPoint.getSignature().getName());
System.out.println("hijacked arguments : " + Arrays.toString(joinPoint.getArgs()));
System.out.println("Around before is running!");
Object ret = joinPoint.proceed();
System.out.println("Around after is running!");
return ret;
}
}
I just cant seem to find the problem.
Everything is declared and spring loads both beans into the container.
If possible. How do I add another function to the same execute deceleration?
Thanks.
Update:
So I have managed to do some progress, apparently I needed to Change project configuration to support Aspectj (in eclipse) but now I am getting the following error:
applying to join point that doesn't return void: method-execution(java.lang.Object com.X.Y.infrastructure.rest.RestClient.get(java.lang.String, java.util.List, java.lang.Class))
I will assume that i need to specify the return argument. I thought * will catch all, but that seems to be for void.
So how does the point cut should look for generics like that?
Thanks again
Another update
Following the comment I have added a return value to the method. But the aspect is still not firing.
By request the spring configuration:
<aop:aspectj-autoproxy />
<bean id="webRequestTimeLoggerAspect" class="com.X.Y.infrastructure.rest.WebRequestTimeLoggerAspect" />
And in fact I don't believe I need to declare a bean for that aspect because I have package scan. But just in case.
Can any one tell me what is the difference between Joinpoint and Proceedingjoinpoint?
When to use Joinpoint and Proceedingjoinpoint in the method of aspect class?
I used the JoinPoint in my AspectJ class like:
#Pointcut("execution(* com.pointel.aop.test1.AopTest.beforeAspect(..))")
public void adviceChild(){}
#Before("adviceChild()")
public void beforeAdvicing(JoinPoint joinPoint /*,ProceedingJoinPoint pjp - used refer book marks of AOP*/){
//Used to get the parameters of the method !
Object[] arguments = joinPoint.getArgs();
for (Object object : arguments) {
System.out.println("List of parameters : " + object);
}
System.out.println("Method name : " + joinPoint.getSignature().getName());
log.info("beforeAdvicing...........****************...........");
log.info("Method name : " + joinPoint.getSignature().getName());
System.out.println("************************");
}
But what I see in other resources is:
#Around("execution(* com.mumz.test.spring.aop.BookShelf.addBook(..))")
public void aroundAddAdvice(ProceedingJoinPoint pjp){
Object[] arguments = pjp.getArgs();
for (Object object : arguments) {
System.out.println("Book being added is : " + object);
}
try {
pjp.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
}
Here what will ProceedingJoinPoint do differently compare to 'JointPoint? Also what willpjp.proceed()` do for us?
An around advice is a special advice that can control when and if a method (or other join point) is executed. This is true for around advices only, so they require an argument of type ProceedingJoinPoint, whereas other advices just use a plain JoinPoint. A sample use case is to cache return values:
private SomeCache cache;
#Around("some.signature.pattern.*(*)")
public Object cacheMethodReturn(ProceedingJoinPoint pjp){
Object cached = cache.get(pjp.getArgs());
if(cached != null) return cached; // method is never executed at all
else{
Object result = pjp.proceed();
cache.put(pjp.getArgs(), result);
return result;
}
}
In this code (using a non-existent cache technology to illustrate a point) the actual method is only called if the cache doesn't return a result. This is the exact way the Spring EHCache Annotations project works, for example.
Another specialty of around advices is that they must have a return value, whereas other advice types must not have one.
#Around("execution(* com.mumz.test.spring.aop.BookShelf.addBook(..))")
It means before calling com.mumz.test.spring.aop.BookShelf.addBook method aroundAddAdvice method is called.
After
System.out.println("Book being added is : " + object); operation is completed . it will call your actual method addBook(). pjp.proceed() will call addBook() method.
Use JoinPoint with following advice types:
#Before, #After, #AfterReturning, #AfterThrowing
Use ProceedingJoinPoint with following advice type:
#Around
ProceedingJoinPoint is a JoinPoint with additional features.
ProceedingJoinPoint is used with #Around advice. #Around is very powerful advice that combines the features of rest of the Advice.
ProceedingJoinPoint::proceed is basically used to execute the original method.
Consider following example:
#Around("#annotation(com.annotations.ExecutionLoggerAround)")
public void executeLogger(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("Method execution starts at :: " + System.currentTimeMillis());
proceedingJoinPoint.proceed();
System.out.println("Method execution completes at :: " + System.currentTimeMillis());
}
#ExecutionLoggerAround
public void generateReport() {
System.out.println("Generating XLS report !!");
}
public #interface ExecutionLogger {
}
Now when you will call generateReport, Around advice will be executed. If you skip line proceedingJoinPoint.proceed(), the actual method will not be executed. You will see only those two println in the console.
PS: To understand better you need to know how AOP works. Spring creates proxy objects either using JDK proxy or CGLIB proxy. So its actually the proxied object that executes at runtime which use our method behind the scene.
I have the following method
#AutoHandling(slot = FunctionalArea.PRE_MAIN_MENU)
#RequestMapping(method = RequestMethod.GET)
public String navigation(ModelMap model) {
logger.debug("navigation");
...
//First time to the Main Menu and ID-Level is ID-1 or greater
if (!callSession.getCallFlowData().isMainMenuPlayed()
&& callSession.getCallFlowData().getIdLevel() >= 1) {
// Call Auto Handling
logger.info("Call AutoHandling");
autoHandlingComponent.processAutoHandling();
}
...
return forward(returnView);
}
Basically what I want to do, is have a pointcut on processAutoHandling()
But in the #After, I need to use the slot() for #AutoHandling
I tried this, but it does not get called
#Pointcut("execution(* *.processAutoHandling())")
public void processAutoHandleCall() {
logger.debug("processAutoHandleCall");
}
#Around("processAutoHandleCall() &&" +
"#annotation(autoHandling) &&" +
"target(bean) "
)
public Object processAutoHandlingCall(ProceedingJoinPoint jp,
AutoHandling autoHandling,
Object bean)
throws Throwable {
...
You can use the wormhole design pattern for this. I am illustrating using AspectJ byte-code based approach and syntax, but you should be able to get the same effect using an explicit ThreadLocal if you are using Spring's proxy-based AOP.
pointcut navigation(AutoHandling handling) : execution(* navigation(..))
&& #annotation(handling);
// Collect whatever other context you need
pointcut processAutoHandleCall() : execution(* *.processAutoHandling());
pointcut wormhole(AutoHandling handling) : processAutoHandleCall()
&& cflow(navigation(handling));
after(AutoHandling handling) : wormhole(hanlding) {
... you advice code
... access the slot using handling.slot()
}
a) It can't work, you are trying to match two different things:
#Around("processAutoHandleCall() &&" +
"#annotation(autoHandling) &&" +
"target(bean) "
)
processHandleCall() matches the inner method execution autoHandlingComponent.processAutoHandling() while #annotation(autoHandling) matches the outer method execution navigation(ModelMap model)
b) since you are obviously trying to advise a Controller, there are a few caveats:
if you use proxy-target-class=true everything should work as is, just make sure you don't have any final methods
if you don't, all your controller methods must be backed by an interface and the #RequestMapping etc annotations must be on the interface, not the implementing class as described in this section of the Spring MVC reference docs
I need to log many classes in some packages in a project which I can not change its source code.
So I need a solution which I can specify package name, and with spring aop add logging to that package's classes without change them but I dont know how can I do that.
How can I do that?
With Spring AOP you can only log these classes if they are used as Spring Beans, and even then you can only log public method executions.
Here is an aspect in #AspectJ notification (this is the style that is compatible with both "real AspectJ" and Spring AOP, read about the difference in the spring reference) that you can use in both Spring AOP and AspectJ byte code weaving:
#Aspect
public class LoggingAspect{
#Pointcut("execution(* com.mycompany.myproject.*.*(..))")
public void methodToLog(){
};
#Around("methodToLog()")
public Object logMethod(final ProceedingJoinPoint joinPoint) throws Throwable{
final StaticPart staticPart = joinPoint.getStaticPart();
final String sig =
"" + staticPart.getSignature() + " with args: "
+ Arrays.deepToString(joinPoint.getArgs());
System.out.println("Entering method " + sig);
final Object result = joinPoint.proceed();
System.out.println("Leaving method " + sig);
return result;
}
}
Here is a stupid class with some methods:
package com.mycompany.myproject;
public class Dummy1{
public static void main(final String[] args){
final Dummy1 dummy = new Dummy1();
dummy.doSomeStuff();
dummy.doSomeStuffWithSomeArgs("Hello", 123);
}
private void doSomeStuff(){}
public void doSomeStuffWithSomeArgs(final String firstArg,
final int secondArg){}
}
When you start this class in Eclipse / AJDT as Java/AspectJ application, you get the following output:
Entering method void com.mycompany.myproject.Dummy1.main(String[]) with args: [[]]
Entering method void com.mycompany.myproject.Dummy1.doSomeStuff() with args: []
Leaving method void com.mycompany.myproject.Dummy1.doSomeStuff() with args: []
Entering method void com.mycompany.myproject.Dummy1.doSomeStuffWithSomeArgs(String, int) with args: [Hello, 123]
Leaving method void com.mycompany.myproject.Dummy1.doSomeStuffWithSomeArgs(String, int) with args: [Hello, 123]
Leaving method void com.mycompany.myproject.Dummy1.main(String[]) with args: [[]]
To test this in Spring AOP would involve more work (the main method approach won't work, you will have to create an ApplicationContext and register a bean of type Dummy1, on which you will call the methods), so I'll leave that to you, but I am pretty sure the private method call will not be logged.
If you download the SpringSource Tool Suite, you get nice tools for aspect visualisation and testing. You should also read the AspectJ book, even if you only want to use Spring AOP. It's a great book.
BTW: you will obviously want to use a real logger, not system.out. You can either define one per aspect, or (only with real aspectj) you can introduce it as a static member in the target class to get per-class logging. A killer feature of AspectJ in my opinion.