事务在Aspectj中不起作用 [英] Transaction doesn't work in 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:
-
您方面的建议
-
@Transactional
注释.实际上我完全不知道这是否行得通(我在Web的方面方法/建议上没有找到使用@Transactional
的示例,但也许我搜索的方式有误),因为Spring中的声明式事务处理是通过基于代理的方式实现的技术,就像Spring AOP一样.阅读关于事务管理的第12章有关更多详细信息,请参见Spring手册,尤其是第12.5.1章.我很确定您会找到一种在其中做所需的方法. - 嵌套交易,例如
UpdateProcessStrategy.preProcess(..)
由非常有建议性的建议调用,该建议旨在进行事务处理,但也被声明为@Transactional
.因此,您在一个事务中有一个事务.我不知道Spring如何处理这个问题,但是也许有关Spring事务传播的指南包含启发性的细节.
LoggingAspect.saveActionMessage(..)
上的@Transactional
annotation on your aspect's adviceLoggingAspect.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屋!