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.
Related
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.
I am autowiring service in controller. And in service, I have a scenario where I need to throw an exception and DB changes also. So, I tried #Async
#Transactional
public void verifyOtp(OtpDto otpDto)
...
if(xyz){
deactivateOtp(emp.getId());
throw new ServException("Mobile no requested is already assigned", "error-code");
}
}
#Async
#Transactional //with or without
public void deactivateOtp(Integer id){
otpRepo.deactivateOtp(id);
}
public interface OtpRepository extends JpaRepository<Otp, Integer> {
#Modifying
#Query("UPDATE Otp SET isActive = 0 WHERE id = :id")
public void deactiveOtp(#Param("id") Integer id);
This is not creating new thread. But, if I gives at repo, it works
public void deactivateOtp(Integer id){
otpRepo.deactivateOtp(id);
}
public interface OtpRepository extends JpaRepository<Otp, Integer> {
#Async
#Transactional
#Modifying
#Query("UPDATE Otp SET isActive = 0 WHERE id = :id")
public void deactiveOtp(#Param("id") Integer id);
First of all check that the service is wrapped into proxy (you can place a breakpoint in controller and see the reference to the service, it will be with proxy). Otherwise there is something wrong with the configuration and #Transactional/#Async won't work till you fix that.
Now, assuming this is not an issue, there is an issue in the code:
When the controller calls service.verifyOtp it goes to the proxy (to handle the transaction) and then to your implementation.
But when it reaches your implementation and you call the method that belongs to the same impl, it doesn't pass through the proxy again, instead it directly goes to the deactivateOtp as if there is no spring at all here. Of course, #Async doesn't work.
In terms of resolution:
Consider using self injection if you work with spring 4.3+. Read this thread for more information.
Alternatively, refactor your code so that the deactivateOtp will be a public method of another class. In this case the call won't be "internal" anymore, it will path through Proxy hence the #Async will work.
This is discussed many times.
Basically, Spring AOP will not intercept local method call
(in your case call deactivateOtp() within the same class)
You can read more about this behavior here: Understanding AOP proxies
Highlight:
The key thing to understand here is that the client code inside the main(..) of the Main class has a reference to the proxy. This means that method calls on that object reference will be calls on the proxy, and as such the proxy will be able to delegate to all of the interceptors (advice) that are relevant to that particular method call. However, once the call has finally reached the target object, the SimplePojo reference in this case, any method calls that it may make on itself, such as this.bar() or this.foo(), are going to be invoked against the this reference, and not the proxy. This has important implications. It means that self-invocation is not going to result in the advice associated with a method invocation getting a chance to execute.
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 {
}
Just came accross this today in a Spring MVC cotnroller class,
#RequestMapping(value = { "/foo/*" }, method = { RequestMethod.GET})
private String doThing(final WebRequest request) {
...
return "jsp";
}
This is making it a bit harder to write a test, I'll probably change it to public but what's the point of allowing mappings on private methods?
Java does not provide a mechanism for limiting the target of annotations based on access modifier.
As #smp7d stated, Java does not limit the target of annotations based on access modifiers, but syntactically speaking, #RequestMapping should not work on private methods. Also we cannot limit this, since it would break the backward compatibility. So, you can either go for defining your methods as public or you can create your own custom implementation.
Take a look at this: Spring's #RequestMapping annotation works on private methods
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.