I have a custom Around implemented to match on a custom Annotation. I want the custom around to execute WITHIN the outer @Transactional. Unfortunately, this doesn't appear to work. (The AOP is working. I see stacktraces that show it).
The stack traces show my AOP executing before (a logger), the MyBatis Session starting a transaction, MyBatis closing the Transactions, Spring closing the transaction and then my AOP completing.
I thought having my AOP implement Ordered would help. I set the value returned to 1. I used . This didn't work. I think it's because I misread how Spring orders.
Advice ordering
What happens when multiple pieces of advice all want to run at the
same join point? Spring AOP follows the same precedence rules as
AspectJ to determine the order of advice execution. The highest
precedence advice runs first "on the way in" (so given two pieces of
before advice, the one with highest precedence runs first). "On the
way out" from a join point, the highest precedence advice runs last
(so given two pieces of after advice, the one with the highest
precedence will run second).
When two pieces of advice defined in different aspects both need to
run at the same join point, unless you specify otherwise the order of
execution is undefined. You can control the order of execution by
specifying precedence. This is done in the normal Spring way by either
implementing the org.springframework.core.Ordered interface in the
aspect class or annotating it with the Order annotation. Given two
aspects, the aspect returning the lower value from Ordered.getValue()
(or the annotation value) has the higher precedence.
When two pieces of advice defined in the same aspect both need to run
at the same join point, the ordering is undefined (since there is no
way to retrieve the declaration order via reflection for
javac-compiled classes). 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 - which can be ordered
at the aspect level.
So I took out the order attribute. This should make @Transactional return Integer.MIN_VALUE. So it should, if I understood the quote above, run last. When I redeployed, it still executed backward. My AOP, Spring TX, MyBatis, Close MyBatis, Close SPring Tx, Close My AOP.
What am I doing wrong?
If the order attribute is not configured for @Transactional annotation, then the Advisor which is responsible for transaction attribute - AbstractPointcutAdvisor
(in fact, one of the subclasses of it) will return Ordered.LOWEST_PRECEDENCE, which is defined as Integer.MAX_VALUE.
The Advisor which is responsible for custom AOP advice, a subclass of the same AbstractPointcutAdvisor, will look whether the actual Advice implements Ordered interface, and if it does, the provided value will be used during the sorting.
If custom AOP advice does not implement Ordered interface, the Advisor returns the same default Ordered.LOWEST_PRECEDENCE and the result of the sorting becomes slightly unpredictable.
So, configuring the order attribute for @Transactional annotation e.g. like this
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd>
.......
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" order="[Order for @Transactional]">
<beans/>
and your custom AOP advice implementation looks like this
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.Ordered;
@Aspect
public class CustomAspect implements Ordered {
@Around(value = "@annotation(CustomAnnotation)")
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
...
}
....
@Override
public int getOrder() {
return [Order for @CustomAnnotation];
}
....
}
then you probably have all the freedom (yet, statically) with the ordering throughout your application.
Under the hood, it's AspectJAwareAdvisorAutoProxyCreator who upon a Proxy initialization sorts the Advisors using the comparator org.springframework.aop.aspectj.autoproxy.AspectJPrecedenceComparator which delegates the sorting to OrderComparator.
Later on, upon the actual execution, a concrete implementation of AopProxy holds for a specific method an array of the advices, whom it calls interceptors, and this might be used for dynamic reordering, I guess, but none of these things seems to me easily accessible and/or configurable.
My environment is Spring Beans, TX, AOP - all version 4.0.3. I also have two custom Transaction Managers, one is Hibernate-bound and one is JDBC DataSource-bound, but I don't think it matters here
After a little experimenting it turns out that simply removing the order attribute does not make this work. I find this odd as the @Transactional default order is Integer.MIN_VALUE. Apparently if you want to enable ordering you have to explicitly set the order to the smallest of all the AOP orders.
From page https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/transaction.html , it says that transaction's default order is Ordered.LOWEST_PRECEDENCE
, which value equal to Integer.MAX_VALUE