I'm newbie to Spring AOP. I do understand the concept behind it and i also do understand the concept of #Before or #After etc usage. What i am so confused is still the usage of Spring AOP. Think of the below method of a class.
public void test(int x) {
:
x++;
logger.info("This is a test" + x);
:
try {
:
} catch (Exception e) {
throw new ...
}
:
}
The old way of capturing the log is as shown above.
Here's my questions:
If i were to implement the above method using Spring AOP, this logger will be removed but then does Spring AOP able to capture this log message? (from what i know Spring AOP does not look inside a method)
If answer to clause 1) is yes then how is it done?
If answer is no what's the point of using Spring AOP. Usage of #Before is useless unless you want to capture the information like parameters prior to execution of the method. Most of the time we want to capture some log inside a method itself.
Forget about AspectJ. I do know that AspectJ can do the above job.
I just want to know what's the point of using Spring AOP if it cannot do the very fundamental thing of capturing logs inside a method.
Any help is appreciated.
Further note:
I assume after implementating Spring AOP the above code would be like this. The logger call is no longer in the test method since it will be taken care of by the aspect class. Isn't that the purpose of AOP? To remove cross cutting concern from objects (since it's not related to the actual service of the object) and be taken care of by the aspect class?
public void test() {
:
try {
:
} catch (Exception e) {
throw new ...
}
:
}
If Spring AOP cannot do this what's the point of having a AOP?
I am having difficulty understanding what you are asking for here. In general, I don't know what it means to 'capture logs inside a method', but I think I can provide some assistance anyways.
It sounds to me like you want to arbitrarily insert code into random points in a method, not necessarily at the beginning or the end of the method. In general, Spring AOP cannot do this, and I am not sure that AspectJ will be able to help with either, but I am less familiar with that to give you a definitive answer.
Spring AOP, like you said, can inject before/after/around various JoinPoints in your codebase. These JoinPoints are going to be methods, and only in Spring managed classes.
So if you have a method like the following, you can add logging (via System.out in this case) around it via an #Around aspect.
The code:
public void test() {
System.out.println("I am in a method now");
}
The aspect:
#Around("execution(public * *(..))")
public void publicMethods(ProceedingJoinPoint pjp) {
System.out.println("before in an aspect");
pjp.proceed();
System.out.println("after in an aspect");
}
This essentially turns the initial method into this (as well as adding these System.out's to all public methods):
public void test() {
System.out.println("before in an aspect");
System.out.println("I am in a method now");
System.out.println("after in an aspect");
}
Depending on the layout of your code, you may be able to effectively insert arbitrarily by creating methods at the points that you want to insert. I wouldn't recommend this, but it certainly is possible.
Finally, here are the answers to your questions:
You could replace the logger with a #Before aspect, assuming the logging line is the first code in the method. If you were to do that, you would then be able to remove the logging from within the method. I don't quite know what you are asking for with the last sentence, but no, Spring AOP does not look 'inside' a method.
Spring AOP is able to 'capture' it because Spring will proxy the class.
The point of Spring AOP is to be able to 'intercept' method calls. You may not see a real use for it, but it is extremely useful. I would beg to differ on the last sentence, when using Spring AOP, I want to be able to examine what is going into my method, or what is coming out.
EDIT:
You are correct, the log call can be removed, and taken care of by the aspect. The thing that must be noted is that the only opportunities for the log method to be called by the aspect are either before or after the actual method invocation.
Related
I have seen numerous examples of Spring functionality related to #Cacheable, #Transactional, #Async, etc. where the same options are reiterated every time:
Self invocation made through a proxy object gotten through either the ApplicationContext.getBean(MyService.class) or an autowired MyService.class proxy object in addition to #Scope(proxyMode=ScopedProxyMode.TARGET_CLASS),
Relocating the target method to a separate #Service class,
Using AspectJ load-time weaving.
While the first two approaches are usually fine, there are times when we need to attach the functionality of the above three (and other) annotations to private methods, whether it be for code clarity, design, or other reasons.
There are many example of the first two approaches, but very few of the last. As I understand, due to the nature of AspectJ LTW, by default it is mutually exclusive with the usual Spring AOP behaviour that enables the #Cacheable, etc. behaviour without much hassle. My questions are as follows:
Are there any decent examples on enabling the above behaviour usually done with the first two options using AspectJ LTW?
Is there a way to enable AspectJ LTW selectively, e.g. not for #Async and #Transactional but just #Cacheable? An example use case of this would perhaps be: due to a team's design, all cacheable methods (some of which may be private) should be located in a facade class which calls private methods that perform heavy calculations, but might update some state (i.e. 'last-queried-at: #') before returning to the external called.
This question is from the viewpoint of a spring-boot user, but I believe it applies generally to Spring AOP and AspectJ LTW. Please correct me if there are special considerations needed in this case.
Are there any decent examples on enabling the above behavior usually done with the first two options using AspectJ LTW?
I have a couple of AspectJ examples on my GitHub account. Both these examples show how to intercept calls within the same target object (self-invocation) and also intercept private methods.
Spring Boot Source Weaving Example with AspectJ
Spring Boot Load-Time Weaving Example with AspectJ
Both the examples are similar except the way aspects are woven into target classes.
Please read the examples' READMEs to find out more about each type of weaving and on how to use each of the examples.
Is there a way to enable AspectJ LTW selectively, e.g. not for #Async and #Transactional but just #Cacheable?
Yes, you can filter based on either of the following:
By the annotation type of the caller method.
#Before("call(* com.basaki.service.UselessService.sayHello(..))" +
" && cflow(#annotation(trx))")
public void inspectMethod(JoinPoint jp,
JoinPoint.EnclosingStaticPart esjp, Transactional trx) {
log.info(
"Entering FilterCallerAnnotationAspect.inspectMethod() in class "
+ jp.getSignature().getDeclaringTypeName()
+ " - method: " + jp.getSignature().getName());
}
By the name of the caller method.
#Before("call(* com.basaki.service.UselessService.sayHello(..))" +
" && cflow(execution(* com.basaki.service.BookService.read(..)))")
public void inspectMethod(JoinPoint jp,
JoinPoint.EnclosingStaticPart esjp) {
log.info(
"Entering FilterCallerMethodAspect.inspectMethod() in class "
+ jp.getSignature().getDeclaringTypeName()
+ " - method: " + jp.getSignature().getName());
}
You can find working examples here.
Updated
Q. Do I understand correctly then, that if I wanted to enable compile-time weaving for transactionality, I would: 1. No longer use a TransactionAwareDataSourceProxy anywhere in my DataSource configuration; 2. Add the following to my application: #EnableTransactionManagement(mode=AdviceMode.ASPECTJ).
Spring AOP and CTW/LTW AspectJ weavings are completely orthogonal, i.e., they are independent of each other.
The compile and LTW modify the actual bytecode, i.e, the lines of code are inserted in the target object's method body.
AOP is proxy-based, i.e. there is a wrapper around the target object. Any call to the target object gets intercepted by the Spring wrapper object. You can also use Spring AOP with CTW/LTW if the need arises. In this case, Spring AOP will make a proxy of the modified target.
You will need #EnableTransactionManagement if you want to enable Spring's annotation-driven transaction management capability.
Q. In your examples, I see that you do not start the application in any special way for CTW. Would this suffice, or have I missed anything?
Yes, in CTW you don't need anything special during start-up since the extra bytecode is already injected in the original code by the AspectJ compiler (ajc) during compile time. For example, here is the original source code:
#CustomAnnotation(description = "Validates book request.")
private Book validateRequest(BookRequest request) {
log.info("Validating book request!");
Assert.notNull(request, "Book request cannot be empty!");
Assert.notNull(request.getTitle(), "Book title cannot be missing!");
Assert.notNull(request.getAuthor(), "Book author cannot be missing!");
Book entity = new Book();
entity.setTitle(request.getTitle());
entity.setAuthor(request.getAuthor());
return entity;
}
Here is the same piece of code after compilation by AspectJ compiler, ajc:
private Book validateRequest(BookRequest request) {
JoinPoint var3 = Factory.makeJP(ajc$tjp_0, this, this, request);
CustomAnnotationAspect var10000 = CustomAnnotationAspect.aspectOf();
Annotation var10002 = ajc$anno$0;
if (ajc$anno$0 == null) {
var10002 = ajc$anno$0 = BookService.class.getDeclaredMethod("validateRequest", BookRequest.class).getAnnotation(CustomAnnotation.class);
}
var10000.inspectMethod(var3, (CustomAnnotation)var10002);
log.info("Validating book request!");
Assert.notNull(request, "Book request cannot be empty!");
Assert.notNull(request.getTitle(), "Book title cannot be missing!");
Assert.notNull(request.getAuthor(), "Book author cannot be missing!");
Book entity = new Book();
entity.setTitle(request.getTitle());
entity.setAuthor(request.getAuthor());
return entity;
}
While in LTW, you need the Java Agent since the code gets modified during load-time, i.e., when the classes are being loaded by Java class loaders.
I read that Spring AOP cannot intercept private and protected methods but it is intercepting them in a weird way why is that?
I have these functions i want to intercept:
public String getName(String string) {
System.out.println("Name : " + name + string);
return name;
}
protected String getNamesprotected(String string) {
System.out.println("Name : " + name + string);
return name;
}
This is my #Aspect code:
#Aspect
public class Logging {
#Before("execution(* com.tutorialspoint.Student.*Name*(..))")
public void beforeAdvice(JoinPoint joinPoint){
System.out.println("Going to setup student profile."+joinPoint.getSignature().toString());
}
}
When this code is executed both getName and getNamesprotected are intercepted but when I execute this code :
#Aspect
public class Logging {
#Before("execution(* com.tutorialspoint.Student.getNamesprotected(..))")
public void beforeAdvice1(JoinPoint joinPoint){
System.out.println("Going to setup student profile."+joinPoint.getSignature().toString());
}
}
Then nothing is intercepted. I also tried replacing getNamesprotected with *getNamesprotected* but still it does not intercept. It only intercepts when *Name* is there.
Can anyone explain me why this is happening?
Because the OP (Prateek Gupta) seemed to be unable (rather unwilling) to create a little SSCCE reproducing the problem and I was just bored during a tea break, I quickly created one by myself with Spring Boot and was very surprised that indeed Spring AOP, in contradiction to the documentation, at least under certain circumstance matches against protected methods when CGLIB proxies are involved.
Thus, I registered myself an account for Spring's Jira issue tracker and reported this regression as SPR-15354. You might want to subscribe for updates on that ticket if interested in answers by the Spring development team.
Update: The person answering to my ticket told me that this is a documentation issue. As SPR-1611 from 2006 tells us, this was already changed on purpose for Spring 1.2.7 but has never found its way into the documentation. Bottom line: Protected methods can be captured via Spring AOP, it is not an accident but has been undocumented for 12 years.
Update 2: The updated documentation text will be in the next Spring release. If you want to read the fixed text today, it is quoted in SPR-1611.
Can anyone explain me why this is happening?
Due to the proxy-based nature of Spring’s AOP framework, protected methods are by definition not intercepted, neither for JDK proxies (where this isn’t applicable) nor for CGLIB proxies (where this is technically possible but not recommendable for AOP purposes).
As a consequence, any given pointcut will be matched against public methods only!
If your interception needs include protected/private methods or even constructors, consider the use of Spring-driven native AspectJ weaving instead of Spring’s proxy-based AOP framework.
When this code is executed both getName and getNamesprotected are
intercepted
#Before("execution(* com.tutorialspoint.Student.*Name*(..))")
Here the preceding wildcard matches methods with any modifier (public, protected, and private) and any return type. The two dots in the argument list match any number of arguments.
Try this it should execute your protected method
#Pointcut("execution(protected * *.*(..))")
Also you can try this if it works for you (I am not 100% sure)
#Before("execution(* com.tutorialspoint.Student+.getNamesprotected(..))")
<prop key="load*">PROPAGATION_REQUIRED, readOnly</prop>
Here the key="load*" uses wildcard "*". It matches every function that starts with load right? My question is that; how am I going to implement this using annotation based configuration without placing it on every function?
#Transactional(readOnly = true, propagation= Propagation.REQUIRED)
Is there anyway I can do this without putting this on top of every function which it's name starts with "load"?
Cheers
You can use aspects for this purpose. I.e. define an aspect which intercepts all methods named as "load*" and ensure transaction is started. However in my opinion, such approach is a bad one since it makes your code hard to unit test, understand and maintain further. In fact #Transactional handling is done using aspects, so why bothering to add another layer of abstraction to this? With this annotation someone will look to your code and immediately know that method is/should be running in transaction. Hiding this will make others life harder.
If you still want to implement it, here is small example. NOTE, that I didn't test this at all, just an idea demonstration.
#Aspect
public class MyInterceptor
{
#Around("execution(* load*(..))")
#Transactional
public Object makeMeTransactional(ProceedingJoinPoint pjp)
{
return pjp.proceed();
}
}
I am having some strange problems using spring and Load-Time-Weaving using AspectJ.
In one of my Apsects, I wanted to react on invocations of the "doAuthentication" Method of the class org.springframework.flex.security3.SpringSecurityLoginCommand. Therefore I created a method:
#Around("execution(* org.springframework.flex.security3.SpringSecurityLoginCommand.doAuthentication(..))")
public Object aroundDoAuthentication(ProceedingJoinPoint pjp) throws Throwable {
...
This aspect is woven correctly if I use the aspectj-weaver agent, but is ignored if I use the spring-weaver. Unfortunately I have to use the spring-weaver, if I want correct aspect-spring integration. The only way I found out to get my aspect woven is to weave it around every method of the target class and to programatically filter the aspect calls:
#Around("execution(* org.springframework.flex.security3.SpringSecurityLoginCommand.*(..))")
public Object aroundDoAuthentication(ProceedingJoinPoint pjp) throws Throwable {
final String methodName = pjp.getSignature().getName();
if("doAuthentication".equals(methodName)) {
...
Using the above code, I managed to weave everythgin correctly, but I am not sattisfied with this as it seems to be a big hack to me.
Could anyone please explain why using the Spring-Weaver I am not able to weave the same as with the aspectj-weaver?
Chris
I don't know the code, but this seams to be the same old question.
Spring AOP is per default proxy based.
This means only invocations of methods that come from outside of the bean go though the proxy. Therefore only this invocation can trigger the AOP advice.
Is it possible to do something like the following:
public void doStuff(#RequirePrivilege("foo") User user) {
// ...
}
and have it effectively run as if it were the following?
public void doStuff(User user) {
if(!user.hasPrivilege("foo"))
throw new UserHasInsufficientPrivileges(); // this is a RuntimeException
// ...
}
I know that Spring has various sorts of AOP support, but the best I could find was AOP code which was annotated so that it would execute before or after a specific method. I want to do the inverse and annotate the code that should be changed.
Ultimately I could just do the above check inside the method, but the annotation way of doing things provides additional documentation which makes it obvious that the user requires a particular privilege without having to keep the documentation in sync with the code.
You can look at using AspectJ for doing this, as it will match on annotations. You can then use an around aspect to decide if the user meets the requirements to use this method.
Spring allows you to use AspectJ, and I would suggest that if possible you not do this at run-time, but at compile-time, as there is no reason to pay the price for using this aspect whenever you start the application. But, if you must do it at run-time then that is doable, to me I try to use compile-time as much as possible.
You may want to look at AspectJ In Action (http://www.manning.com/laddad2/) but here is an example from there:
Signature pattern:
* *(#RequestParam
(#Sensitive *))
Description
*Any method with one parameter marked with the #RequestParam annotations and the parameter’s type is marked with the #Sensitive annotation.*
Example
void create(#RequestParam
MedicalRecord mr), assuming
MedicalRecord carries the
#Sensitive annotation.
I'm certain that your "insufficient privileges" example can be done with Spring AOP, because that's how Spring Security works. You can do some very sophisticated things with around advice and AspectJ.