We're using Hystrix like so:
#HystrixCommand(...)
public void someOperation() {
...
}
This works great. We'd like to be able to inject errors and sleeps for testing within these methods and we're trying to create an #Aspect for just this purpose:
#Before("execution(* our.package.OurClass.someOperation(..))")
public void causeTrouble() {
...
}
The issue we're seeing is that our advice is running before the Hystrix advice (HystrixCommandAspect) which means that sleeps and exceptions we inject in are treated differently and not handled by Hystrix. Is there a way to ensure our aspect runs inside the Hystrix aspect? I've tried the ordering suggestions in the spring documentation without any luck (see http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html#aop-ataspectj-advice-ordering). Is there another approach to solve this? Thanks.
Related
Working with Springboot 2.7.0. I had a a working application and I made these changes on top of it
Aspect Configuration
#Configuration
#EnableAspectJAutoProxy
#ComponentScan
public class AspectConfig {}
Aspect Interface
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Timed { }
Aspect Class to Measure method execution time
#Around("#annotation(Timed)")
public Object measureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object proceed = joinPoint.proceed();
LOG.info("Time taken for {} is {} ms, joinPoint.getSignature(), System.currentTimeMillis() - start,);
return proceed;
}
Added the new #Timed annotation to an existing method in a bean (omitting non relevant code)
#Component
#ConditionalOnExpression("${oauth.enabled}")
public class JwtAuthFilter extends OncePerRequestFilter {
#Timed
public boolean verifySignatureAndExpiry(String bearerToken){
// method logic
}
}
This causes the Springboot application to fail startup.
I can get it to start if I add #Aspect to the JwtAuthFilter class.
but why would I need to do that? It makes the #Timed annotation limited use if I have to annotate every class that needs to use it with #Aspect. Not to mention, though there are no errors, the functionality won't work because an Aspect cannot work on another Aspect.
#Timed works on my controller method though.
#RestController
#RequestMapping(value = "/api/v1", produces = MediaType.APPLICATION_JSON_VALUE)
public class HealthController {
#GetMapping("/health")
#Timed
public Map<String, String> health(){
return Map.of("status", "up");
}
}
This causes the Spring Boot application to fail startup.
You should always post error messages and relevant stack traces, not just say "fails to start up". You are lucky that in this case, I remember the situation, so I can answer your question. Normally, I would be unable to do so without further information.
I can get it to start if I add #Aspect to the JwtAuthFilter class.
That does not make any sense. Why would you add #Aspect to something which is not an aspect? Of course, it makes the start-up error go away, but it also makes your real aspect not fire, because one Spring AOP aspect cannot advise another one, as you already mentioned. Therefore, this approach is - with all due respect - complete nonsense.
The reason for the exception is: You cannot advise your filter by Spring AOP, because it is derived from GenericFilterBean, which has some final methods. Final methods cannot be overriden, therefore not be proxied either. This has the effect of those methods being called upon the proxy instance directly instead of being delegated to the target object, i.e. if such a method accesses an instance field, it shall find it uninitialised, because the proxy's fields are not meant to be initialised, only the target object's ones. See also my answer here for more info.
In this case, final method org.springframework.web.filter.GenericFilterBean#init is trying to access this.logger, which leads to the NPE which makes Spring Boot's Tomcat fail to start up. This has been reported and briefly explained in this comment in Spring issue #27963, which has been closed as invalid.
#Timed works on my controller method though.
Yes, because your controller does not have a problem with accessing an instance field from a final method.
If you absolutely think that you need to measure your filter method's execution time from an aspect, you can switch from Spring AOP to native AspectJ, either for the whole project via load-time weaving or selectively for some target classes via compile-time weaving. I have tried locally, it works with the right pointcut. Then you can also advise your filter. FYI, the pointcut would be something like:
// Annotated class
#Around("execution(* *(..)) && !within(MyAspect) && #target(Timed)")
// Annotated method
#Around("execution(* *(..)) && !within(MyAspect) && #annotation(Timed)")
AspectJ is more powerful than Spring AOP, so you explicitly need to limit matching to method executions, otherwise other joinpoints such as method calls, constructor calls and others would be affected, too. You also need to make sure that the aspect does not advise itself or other aspects, which is perfectly possible in AspectJ.
I have a branch of Test cases that are using the same code repeatedly for different test scenarios/methods. It's like infra related maintenance code. Suppose need to validate some business logic before we need to perform some infra-related operation. Same after the execution of business logic like cleaning all the infra-related configs. Thought we can use Aspects. As our main services are written in spring boot.
#TestInstance(Lifecycle.PER_CLASS)
#TestMethodOrder(OrderAnnotation.class)
#EnableAspectJAutoProxy(proxyTargetClass=true)
public class RegressionTest{
#Test
#Order(1)
public void Test1(){
}
#Test
#Order(2)
public void Test2(){
}
#Test
#Order(3)
public void Test3(){
}
#Test
#Order(4)
public void Test4(){
}
#Test
#Order(5)
public void Test5(){
}
#Test
#Order(6)
public void Test6(){
}
}
In the above example suppose want to run some common code before and after some test cases with the different values( 1 , 5 use some maintenance code with a different activity, and 2,6 use some different ). Depends on the test case. I planning to use Aspect so that no need to write same code repeatedly. Please suggest if you have a better suggestion or how can I use aspect-oriented programming in this scenario.
You can only apply Spring AOP to Spring beans/components. I.e., your test would need to e.g. have a #Component annotation in order to be picked up by a correspondingly configured component scan or be instantiated in a #Bean factory method. I am not a Spring user, i.e. I do not have much experience in using Spring from tests either. But what I do know is that you could simply use native AspectJ, either applied by compile-time or load-time weaving, in order to achieve what you want. That would be pretty straightforward to implement. I can certainly help you, if you present an MCVE on GitHub, so I do not have to start from scratch.
If you want to use a non-AOP solution, why not simply use the existing #BeforeAll and/or #BeforeEach annotations, just like #asafb suggested before? I see no reason why that would not work.
We're using Spring 4.x, Hibernate 5.x, Spring-Data 1.11 and we currently have a SQL interceptor that extends org.hiberate.EmptyInterceptor and we basically manually hook that up at the start of the web request using HibernateInterceptor.setInterceptor. We also have jobs that run in the background via Spring task scheduler. These start their own transactions that obviously don't get the interceptor attached to them. What I'm trying to do at this point is to find a way to intercept Spring's #Transactional in all cases.
I've looked into TransactionInterceptor, and #TransactionalEventListener and so far haven't gotten any of them to work, and it's hard to figure out what is currently considered best practice with Spring.
So basically the problem we're trying to solve is that a the end of a transaction we need to know if it failed or succeeded.
So what is the current best practice with Spring to always get pre/post commit events so we can respond as needed?
edit
Realized that the #TransactionalEventListener wouldn't work as we're not using Spring events so it was just a misunderstanding on my part of what that really did.
One way you could do it is to create a simple aspect, something like:
#Aspect
#Component
public class AfterTransactionalAspect {
#After("#annotation(Transactional)")
public void cleanupAfterTransaction(JoinPoint joinPoint) throws Throwable {
// ... Do cleanup work here
}
Another good way to go (if you're using Spring-destined events) would be to use the #TransactionalEventListener.
Are you sure you're using an ApplicationEventPublisher to publish the events? Do you for sure have #EnableTransactionManagement on your config, #TransactionalEventListener is on a public method, TransactionTemplate is set, using #Transactional on the method the publish event occurs?
I have a class that is using Spring AOP framework in my web app just like the code shown below. I was wondering why the Spring AOP was able to trace add() but not able trace multiple() if I implement the following code.
public interface calculator {
public void add();
public void multiple();
}
public class calculatorImpl implements calculator {
public void add() {
multiple();
}
public void multiple() {}
}
I've did an experiment and found out that the following code is working fine. Meaning that the Spring AOP able to trace both add and multiple function.
ICalculator calcProxy = (ICalculator) context.getBean("calculatorProxy");
calcProxy.add();
calcProxy.multiple();
I think that must be cause by the multiple() was inject by the proxy bean whereas the multiple() in calculatorImpl class wasn't, thus Spring AOP wasn't able to trace it. Correct me if I am wrong.
My next question. Is there a work around on this issue where the add() really need to execute multiple() and get trace by the Spring AOP?
Spring AOP doesn't change the actual method, but adds a proxy around the Object. Once you are inside the inner Object (inside the add() method), there is no proxy, you are underneath it.
I have explained this mechanism in more detail in this past answer:
https://stackoverflow.com/a/4171649/342852
There are three ways to deal with that situation:
Use AspectJ, not Spring AOP. AspectJ compiles the behavior into your code, whereas Spring AOP works with proxies that surround your code.
Access the Proxy from inside the Bean. This is reeeeaaally ugly, but it can be done.
Example code (here you make the class aware of it's being proxied, which is contrary to everything AOP stands for):
public void add() {
((Calculator)AopContext.currentProxy()).multiple();
}
Slightly less horrible (here you make the class aware that it's being managed by Spring):
#Autowired
private ApplicationContext context;
public void add() {
context.getBean(Calculator.class).multiple();
}
Change your design. Example: move the two methods to separate beans and inject each into the other bean.
My project is based on spring framework 2.5.4. And I try to add aspects for some controllers (I use aspectj 1.5.3).
I've enabled auto-proxy in application-servlet.xml, just pasted these lines to the end of the xml file:
<aop:aspectj-autoproxy />
<bean id="auditLogProcessor" class="com.example.bg.web.utils.AuditLogProcessor" />
Created aspect:
package com.example.bg.web.utils;
import org.apache.log4j.Logger;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
#Aspect
public class AuditLogProcessor
{
private final static Logger log = Logger.getLogger(AuditLogProcessor.class);
#After("execution(* com.example.bg.web.controllers.assets.AssetThumbnailRebuildController.rebuildThumbnail(..))")
public void afterHandleRequest() {
log.info("test111");
}
#After("execution(* com.example.bg.web.controllers.assets.AssetThumbnailRebuildController.rebuildThumbnail(..))")
public void afterRebuildThumbnail() {
log.info("test222");
}
}
My controllers:
class AssetAddController implements Controller
class AssetThumbnailRebuildController extends MultiActionController
When I set brake points in aspect advisors and invoke controllers I catch only afterHandleRequest() but not afterRebildThumbnail()
What did I do wrong?
NOTE
I'm asking this question on behalf of my friend who doesn't have access to SO beta, and I don't have a clue what it's all about.
EDIT
There were indeed some misspellings, thanks Cheekysoft. But the problem still persists.
Your breakpoints aren't being hit because you are using Spring's AOP Proxies. See understanding-aop-proxies for a description of how AOP Proxies are special.
Basically, the MVC framework is going to call the handleRequest method on your controller's proxy (which for example the MultiActionController you're using as a base class implements), this method will then make an "internal" call to its rebuildThumbnail method, but this won't go through the proxy and thus won't pick up any aspects. (This has nothing to do with the methods being final.)
To achieve what you want, investigate using "real" AOP via load time weaving (which Spring supports very nicely).
AspectJ doesn't work well with classes in the Spring Web MVC framework. Read the bottom of the "Open for extension..." box on the right side of the page
Instead, take a look at the HandlerInterceptor interface.
The new Spring MVC Annotations may work as well since then the Controller classes are all POJOs, but I haven't tried it myself.
The basic setup looks ok.
The syntax can be simplified slightly by not defining an in-place pointcut and just specifying the method to which the after-advice should be applied. (The named pointcuts for methods are automatically created for you.)
e.g.
#After( "com.example.bg.web.controllers.assets.AssetAddController.handleRequest()" )
public void afterHandleRequest() {
log.info( "test111" );
}
#After( "com.example.bg.web.controllers.assets.AssetThumbnailRebuildController.rebuildThumbnail()" )
public void afterRebuildThumbnail() {
log.info( "test222" );
}
As long as the rebuildThumbnail method is not final, and the method name and class are correct. I don't see why this won't work.
see http://static.springframework.org/spring/docs/2.0.x/reference/aop.html
Is this as simple as spelling? or are there just typos in the question?
Sometimes you write rebuildThumbnail and sometimes you write rebildThumbnail
The methods you are trying to override with advice are not final methods in the MVC framework, so whilst bpapas answer is useful, my understanding is that this is not the problem in this case. However, do make sure that the rebuildThumbnail controller action is not final
#bpapas: please correct me if I'm wrong. The programmer's own controller action is what he is trying to override. Looking at the MultiActionController source (and its parents') the only finalized method potentially in the stack is MultiActionController.invokeNamedMethod, although I'm not 100% sure if this would be in the stack at that time or not. Would having a finalized method higher up the stack cause a problem adding AOP advice to a method further down?