事务在Aspectj中不起作用 [英] Transaction doesn't work in aspectj

查看:94
本文介绍了事务在Aspectj中不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我具有应该在db中记录操作(创建,更新,删除)的方面(请参见下文).取决于操作记录发生在preProcess或postProcess方法中.如果通过这些操作发生一些失败,我不应该记录任何内容. IE.如果创建未发生,则无需对其进行记录.

I have the aspect(see below) which should log actions(create, update, delete) in db. Depends on action logging happens in a preProcess or postProcess method. I shouldn't log anything if some fail happens through these actions. I.e. if create didn't happened, then there is no need to logging it.

我试图对其进行测试.我在连接点中抛出RunTimeException,并期望db中没有新的日志.不幸的是,尽管连接点有异常,但仍保存了新日志.

I tried to tested it. I throw RunTimeException in the join point and expect that there is no new log in db. Unfortunately, new log is saved in spite of exception in the join point.

方面:

@Component
@Aspect
public class LoggingAspect {
    @Autowired
    private ApplicationContext appContext;
    @Autowired
    private LoggingService loggingService;

    @Around("@annotation(Loggable)")
    @Transactional
    public void saveActionMessage(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature ms = (MethodSignature) joinPoint.getSignature();
        Loggable m = ms.getMethod().getAnnotation(Loggable.class);
        LoggingStrategy strategy = appContext.getBean(m.strategy());
        Object argument = joinPoint.getArgs()[0];
        strategy.preProcess(argument);
        joinPoint.proceed();
        strategy.postProcess(argument);
    }
}

TestApplicationConfig:

TestApplicationConfig:

<context:spring-configured/>
    <import resource="applicationConfig-common.xml"/>
    <import resource="applicationConfig-security.xml"/>
    <aop:aspectj-autoproxy/>

    <util:map id="testValues">
        <entry key="com.exadel.mbox.test.testSvnFile" value="${svnFolder.configPath}${svnRoot.file[0].fileName}"/>
        <entry key="com.exadel.mbox.test.testCommonRepositoryPath" value="${svnRoot.commonRepositoryPath}"/>
        <entry key="com.exadel.mbox.test.testMailFile" value="${mailingList.configPath}"/>
    </util:map>

    <context:component-scan base-package="com.exadel.report.common" />

    <!-- Jpa Repositories -->
    <jpa:repositories base-package="com.exadel.report.common.dao" />

    <tx:annotation-driven proxy-target-class="true"
                          transaction-manager="txManager" mode="aspectj"/>

    <bean id="txManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- Data Source -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="org.hsqldb.jdbcDriver" />
        <property name="url" value="jdbc:hsqldb:mem:testdb" />
        <property name="username" value="sa" />
        <property name="password" value="" />
    </bean>

    <!-- Entity Manager -->
    <bean id="entityManagerFactory"
          class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="true"/>
                <property name="generateDdl" value="true"/>
                <property name="databasePlatform" value="org.hibernate.dialect.HSQLDialect"/>
            </bean>
        </property>
        <property name="persistenceUnitName" value="exviewer-test"/>
    </bean>

    <!-- Transaction Manager -->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>

[更新]

LoggingStrategy:

LoggingStrategy:

public interface LoggingStrategy {
    public void preProcess(Object obj);
    public void postProcess(Object obj);
}

BaseLoggingStrategy:

BaseLoggingStrategy:

public class BaseLoggingStrategy implements LoggingStrategy {
    @Override
    public void preProcess(Object obj) {}

    @Override
    public void postProcess(Object obj) {}
}

UpdateProcessStrategy:

UpdateProcessStrategy:

@Service
public class UpdateProcessStrategy extends BaseLoggingStrategy {
    @Autowired
    private LoggingService loggingService;
    @Autowired
    private UserService userService;
    @Autowired
    DeviceService deviceService;
    private Device currentDevice;

    @Override
    @Transactional
    public void preProcess(Object obj) {
        currentDevice = (Device) obj;
        Device previousDevice = deviceService.getById(currentDevice.getId());
        String deviceDataBeforeUpdate = deviceService.getDeviceDetailsInJSON(previousDevice);
        String deviceDataAfterUpdate = deviceService.getDeviceDetailsInJSON(currentDevice);

        String login = userService.getCurrentUser().getLogin();
        String actionMessage = LoggingMessages.DEVICE_UPDATE.name();

        loggingService.save(
                new Logging(
                        login,
                        actionMessage,
                        deviceDataBeforeUpdate,
                        deviceDataAfterUpdate,
                        new Date())
        );
    }

    @Override
    public void postProcess(Object obj) {}
}

aspcet拦截的类:

Class intercepted by aspcet:

@Service
public class DeviceService {
    @Loggable(value = LoggingMessages.DEVICE_CREATE, strategy = CreateProcessStrategy.class)
    @Transactional
    public void create(Device device) {
        createOrUpdate(device);
    }

    @Loggable(value = LoggingMessages.DEVICE_UPDATE, strategy = UpdateProcessStrategy.class)
    @Transactional
    public void update(Device device) {
        createOrUpdate(device);
    }

    private void createOrUpdate(Device device) {
        deviceRepository.save(device);        
    } 

    @Loggable(value = LoggingMessages.DEVICE_REMOVE, strategy = RemoveProcessStrategy.class)
    public void remove(Long deviceId) {
        deviceRepository.delete(deviceId);
    }
}

可记录注释:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Loggable {
    LoggingMessages value();
    Class<? extends LoggingStrategy> strategy();
}

更新操作日志包含: id,created_dtm,action(DEVICE_UPDATE),device_data_before_action_on_the_device(以json格式),device_data_after_action_on_the_device(以json格式),created_by.

Log for update action contains: id, created_dtm, action(DEVICE_UPDATE), device_data_before_action_on_the_device(in json format), device_data_after_action_on_the_device(in json format), created_by.

推荐答案

免责声明:实际上,我不是Spring专家,也许其他人可以在这里为您提供帮助.我的专长是AspectJ,这就是我如何找到您的问题.

Disclaimer: Actually I am not a Spring expert, maybe someone else can help you out here. My field of expertise it AspectJ, which is how I found your question.

无论如何,这里有两个问题:

Anyway, you have two issues here:

    您方面的建议LoggingAspect.saveActionMessage(..)上的
  • @Transactional注释.实际上我完全不知道这是否行得通(我在Web的方面方法/建议上没有找到使用@Transactional的示例,但也许我搜索的方式有误),因为Spring中的声明式事务处理是通过基于代理的方式实现的技术,就像Spring AOP一样.阅读关于事务管理的第12章有关更多详细信息,请参见Spring手册,尤其是第12.5.1章.我很确定您会找到一种在其中做所需的方法.
  • 嵌套交易,例如UpdateProcessStrategy.preProcess(..)由非常有建议性的建议调用,该建议旨在进行事务处理,但也被声明为@Transactional.因此,您在一个事务中有一个事务.我不知道Spring如何处理这个问题,但是也许有关Spring事务传播的指南包含启发性的细节.
  • @Transactional annotation on your aspect's advice LoggingAspect.saveActionMessage(..). Actually I have no idea if this works at all (I found no example using @Transactional on an aspect method/advice on the web, but maybe I searched in the wrong way) because declarative transaction handling in Spring is implemented via proxy-based technology, just like Spring AOP. Read the chapter 12 about transaction management in the Spring manual for further details, especially chapter 12.5.1. I am pretty sure you will find a way to do what you want there.
  • Nested transactions, because e.g. UpdateProcessStrategy.preProcess(..) is called by the very advice which is meant to be transactional, but is declared @Transactional too. So you have a transaction within a transaction. How Spring handles this, I have no idea, but maybe this tutorial about Spring transaction propagation contains enlightening details.

Spring手册列出了几种实现事务行为的方法:以编程方式,通过注释性地声明性地,基于XML的<tx:advice>内容等等.我不知道哪种方法最适合您,我只想提供一些一般性提示.

The Spring manual lists several means to implement transactional behaviour: programmatically, declaratively via annotations, XML-based <tx:advice> stuff and so forth. I don't know which way is the best for you, I merely wanted to provide some general hints.

这篇关于事务在Aspectj中不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆