Search code examples
javaspringaopaspectjspring-aop

Pass object between before and after advice


My question is the same as in Pass object between before and after advice?, however the accepted answer doesn't work in my case and I would like to ask for some clarification on this topic.

Actually, I'm using around advice, but I have to change it. Let's consider an example:

public Object registerLog( ProceedingJoinPoint jpoint)
{
    SomeObject so = getSomeData( jpoint.getArgs());
    Object result = jpoint.proceed();
    getMoreData( result, so);
    log( so);
}

My proceed() method is inserting object into database. In this scenario I'm logging the result of INSERT before its transaction commits, which is not very good. That's why I want to split around advice into before and after, so I can log in after, when the record is already in database. However, I still need to get some data of the object before it's stored in database.

Of course the solution must be thread-safe, so I can't just add a SomeObject member to my aspect class. From the linked question I learned about ThreadLocal, and so far it's the best solution I found. However, I never used it before and I would like to ask if there are any other solutions. Maybe there's a way to force a transaction to commit while processing an aspect (I'm not sure right now if this is even a good idea)?

UPDATE with more details

I have two pointcuts:

<aop:pointcut id="mngrPointcut" expression="execution(* com.mngr.Foo.*(..))"/>
<aop:pointcut id="savePointcut" expression="execution(* com.mngr.Foo.save(..))"/>

First pointcut has advice

<aop:advisor advice-ref="txAdvice" pointcut-ref="mngrPointcut"/>

where advice is

<tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
        <tx:method name="select*,search*,get*" propagation="REQUIRED" rollback-for="Exception" read-only="true"/>
        <tx:method name="*" propagation="REQUIRED" rollback-for="Exception"/>
    </tx:attributes>
</tx:advice>

For second pointcut I have aspect

<aop:aspect ref="logAspect">
    <aop:around method="registerLog" pointcut-ref="savePointcut"/>
</aop:aspect>

When calling com.mngr.Foo.save() both pointcuts are valid. Probably because of declaration order, mngrPointcut is triggered first, starting a new transaction. Then savePointcut is triggered, starting an aspect. As I mentioned above, aspect is responsible for logging. Because of the order in which pointcuts are triggered, object is logged before it is stored in database, which is wrong. I thought that I have to use before and after advice instead of around, so I can call log() method in after part, when the transaction is already comitted.


Solution

  • Thanks to Nándor Előd Fekete's comment, I solved the problem by setting order of transaction and advice. Lower order force aspect to run earlier on the way in and in reverse on the way out. Not ordered aspects run after the one with order.