交易在aspectj中不起作用

时间:2014-12-22 14:35:56

标签: aspectj spring-transactions

我有方面(见下文),它应该记录db中的操作(创建,更新,删除)。取决于在preProcess或postProcess方法中发生的操作日志记录。如果通过这些操作发生一些失败,我不应该记录任何事情。即如果创建没有发生,那么就没有必要记录它。

我试过测试它。我在连接点抛出RunTimeException,并期望db中没有新的日志。不幸的是,尽管连接点存在异常,仍会保存新日志。

方面:

@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:

<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:

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

BaseLoggingStrategy:

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

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

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拦截的类:

@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。

1 个答案:

答案 0 :(得分:0)

免责声明: 其实我不是Spring专家,也许其他人可以帮助你。我的专业领域是AspectJ,这就是我发现你的问题的方式。

无论如何,你有两个问题:

    关于您方面的建议的{li> @Transactional注释LoggingAspect.saveActionMessage(..)。实际上我根本不知道这是否有效(我发现在网络上的方面方法/建议上没有使用@Transactional的例子,但也许我用错误的方式搜索)因为Spring中的声明式事务处理是通过代理实现的基于Spring AOP的技术。请阅读Spring手册中的chapter 12 about transaction management以获取更多详细信息,尤其是chapter 12.5.1。我很确定你会找到一种方法来做你想做的事。
  • 嵌套交易,因为例如UpdateProcessStrategy.preProcess(..)被称为事务性的建议调用,但也被声明为@Transactional。所以你在交易中有一个交易。 Spring如何处理这个问题,我不知道,但也许这个tutorial about Spring transaction propagation包含启发性的细节。

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