开发者

@Transactional on aspect advice possible?

开发者 https://www.devze.com 2023-02-22 17:16 出处:网络
Can I apply the @Transactional tag to an aspect advice?I\'m trying to wrap all calls to the service layer (com.mycompany.app.myapp.service.*) in a transaction using aspects.My aspect is properly inter

Can I apply the @Transactional tag to an aspect advice? I'm trying to wrap all calls to the service layer (com.mycompany.app.myapp.service.*) in a transaction using aspects. My aspect is properly intercepting the calls to the service layer, but I can't figure out how to start a transaction. I thought I could apply the @Transactional tag and because I've got the tag, it'd pick it up and begin the transaction. What am I missing?

XML configuration:

<bean id="systemArchitectureAspect" class="com.mycompany.app.myapp.aspect.SystemArchitecture"/>
<bean id="transactionAspect" class="com.mycompany.app.myapp.aspect.MyAspect"/>

<tx:annotation-driven transaction-manager="transactionManager" />

<bean id="transactionManager"  
    class="org.springframework.transaction.jta.JtaTransactionManager"> 
    <property name="transactionManager" ref="AtomikosTransactionManager" /> 
    <property name="userTransaction" ref="AtomikosUserTransaction" /> 
</bean> 

<bean id="AtomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"  
 开发者_Python百科   init-method="init" destroy-method="close"> 

    <property name="forceShutdown" value="false" /> 
</bean> 

<bean id="AtomikosUserTransaction"  
    class="com.atomikos.icatch.jta.UserTransactionImp"> 
    <property name="transactionTimeout" value="10" />
</bean> 

<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="*" propagation="REQUIRED" />
    </tx:attributes>
</tx:advice>

Aspect w/pointcuts:

package com.mycompany.app.myapp.aspect;

@Aspect
public class SystemArchitecture {
    @Pointcut( "execution(* com.mycompany.app.myapp.service..*.*(..))" )
    public void inServiceLayer() {};

    @Pointcut( "execution(* com.mycompany.data..*.*(..))" )
    public void inDataAccessLayer() {};
}

The advice I'm trying to apply to my pointcuts:

package com.mycompany.app.myapp.aspect;

@Aspect
public class TransactionAspect {

    @Transactional
    @Around( "com.mycompany.app.myapp.aspect.SystemArchitecture.inServiceLayer()" )
    public Object interceptServiceLayer( ProceedingJoinPoint pjp ) throws Throwable
    {
        return pjp.proceed();
    }
}


Below I have an example that shows how you can use @Transactional together with your inServiceLayer() Pointcut. I have chosen to separate the normal flow from the exception flow. That is why I do not use the @Around advice.

@Aspect
public class TransactionAspect {
    private TransactionService transactionService = new TransactionServiceNull();

    @Pointcut( "execution(* com.mycompany.app.myapp.service..*.*(..))" )
    public void inServiceLayer() {};

    @Pointcut("execution(@org.springframework.transaction.annotation
        .Transactional * *(..))")
    public void transactionalMethod() {}

    @Before("transactionalMethod() && inServiceLayer()")
    public void beforeTransactionalMethod(JoinPoint joinPoint) {
        transactionService.beginTransaction();
    }

    @AfterReturning("transactionalMethod() && inServiceLayer()")
    public void afterTransactionalMethod(JoinPoint joinPoint) {
        transactionService.commit();
    }

    @AfterThrowing(pointcut = "transactionalMethod() && inServiceLayer()", 
         throwing = "e")
    public void afterThrowingFromTransactionalMethod(JoinPoint joinPoint, 
         RuntimeException e) {
        transactionService.rollback();
    }

    public void setTransactionService(
        final TransactionService transactionService) {
        this.transactionService = transactionService;
    }
}

After a quick look on your code I have to ask why you have annotated your Pointcut with @Transactional? You should only mark your business methods that you want to be executed in a transaction with that.

I hope this helps!


As @Espen said you should apply @Transactionalon your business methods directly as the Annotation itself causes Spring to create an Aspect that applies transactions to your method. So there is no need to create an aspect manually.

However, if you want to apply transactions to all you service methods and whatever else you selected with those pointcuts you should do use the xml configuration to create the transactions. Look for declarative transaction management in the documentation

Also I don't think you can apply @Transactional to an Advice. At least it is not working for me.


Spring transaction annotation at run time creates a proxy object. So if you apply transactional annotation on an advice which is advicing the service then the transaction will be for the advice and not for the service since the advice works on a proxy object of the service and your transactional annotation would work on a proxy object of the advice and not the main method of the advice. Ideally you should not be having an advice which is an extension of the functionality of the service. This defeats the purpose of the proxy pattern.


robgmills

   @Transactional
    @Around( "com.mycompany.app.myapp.aspect.SystemArchitecture.inServiceLayer()" )
    public Object interceptServiceLayer( ProceedingJoinPoint pjp ) throws Throwable
    {
        return pjp.proceed();
    }

You can use above Around advice but need to do couple of small changes.

  1. Add (propagation = Propagation.REQUIRES_NEW) to @Transactional in above.
  2. Add @Transactional annotation to the service method for which you have added the pointcut inServiceLayer().
0

精彩评论

暂无评论...
验证码 换一张
取 消