我有一个名为 NotificationService 的实用程序服务。它遵循 MailService 的思路,服务层具有 NotificationService 接口(定义服务)和 NotificationServiceUtil 类(静态提供服务),并且我有一个提供 NotificationServiceImpl(实现)的 impl 层。然后我在 ext-spring.xml 中注册了服务:
<bean id="org.mitre.asias.portal.notification.service.NotificationService"
class="org.mitre.asias.portal.notification.service.impl.NotificationServiceImpl">
...
</bean>
<bean id="org.mitre.asias.portal.notification.service.NotificationServiceUtil"
class="org.mitre.asias.portal.notification.service.NotificationServiceUtil">
<property name="service"
ref="org.mitre.asias.portal.notification.service.NotificationService" />
</bean>
一切都按预期进行,直到我尝试将交易混入其中。我在 NotificationServiceImpl 中有一个需要事务性的方法:
public void sendAndUpdate( Message message, List<Event> eventsToUpdate ) throws SystemException, MessagingException {
for ( Event event : eventsToUpdate ) {
eventLocalService.updateEvent( event );
}
Transport.send( message );
}
这个想法是,如果由于某种原因消息发送失败,对模型对象的更改将被回滚,因此我可以稍后重试发送。我尝试使用以下方法注释此方法:
@Transactional( isolation = Isolation.PORTAL,
rollbackFor = { PortalException.class, SystemException.class, MessagingException.class } )
但它没有采取。我尝试将该注释移动到 NotificationService 接口,但仍然没有骰子。我启用了调试日志记录,org.hibernate.transaction.JDBCTransaction
并且可以看到对该方法的调用从未启动事务,但是为updateEvent
循环中的每个调用启动并提交了一个事务。
深入研究源代码,Liferay 似乎有一个名为的 bean 后处理器ServiceBeanAutoProxyCreator
,其中包含以下代码:
...
protected Object[] getAdvicesAndAdvisorsForBean(
Class<?> beanClass, String beanName, TargetSource targetSource)
throws BeansException {
if (beanName.endsWith(_SERVICE_SUFFIX)) {
return PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS;
}
else {
return DO_NOT_PROXY;
}
}
private static final String _SERVICE_SUFFIX = "Service";
...
这使它看起来好像每个名称以 Service 结尾的 bean(就像我的 bean 所做的那样:)都...id="org.mitre.asias.portal.notification.service.NotificationService"...
应该包装在ServiceBeanAopProxy
根据生成的服务构建器添加事务建议的bean 中base-spring.xml
:
<bean class="com.liferay.portal.spring.aop.ServiceBeanAutoProxyCreator">
<property name="methodInterceptor" ref="serviceAdvice" />
</bean>
<bean class="com.liferay.portal.spring.context.PortletBeanFactoryCleaner" />
<bean class="com.liferay.portal.spring.context.PortletBeanFactoryPostProcessor" />
<bean class="com.liferay.portal.spring.bean.BeanReferenceAnnotationBeanPostProcessor" />
<bean id="portletClassLoader" class="com.liferay.portal.kernel.portlet.PortletClassLoaderUtil" factory-method="getClassLoader" />
<bean id="servletContextName" class="com.liferay.portal.kernel.portlet.PortletClassLoaderUtil" factory-method="getServletContextName" />
<bean id="basePersistence" abstract="true">
<property name="dataSource" ref="liferayDataSource" />
<property name="sessionFactory" ref="liferaySessionFactory" />
</bean>
<bean id="serviceAdvice" class="com.liferay.portal.monitoring.statistics.service.ServiceMonitorAdvice">
<property name="monitoringDestinationName" value="liferay/monitoring" />
<property name="nextMethodInterceptor" ref="asyncAdvice" />
</bean>
<bean id="asyncAdvice" class="com.liferay.portal.messaging.async.AsyncAdvice">
<property name="defaultDestinationName" value="liferay/async_service" />
<property name="nextMethodInterceptor" ref="threadLocalCacheAdvice" />
</bean>
<bean id="threadLocalCacheAdvice" class="com.liferay.portal.cache.ThreadLocalCacheAdvice">
<property name="nextMethodInterceptor" ref="transactionAdvice" />
</bean>
<bean id="transactionAdvice" class="com.liferay.portal.spring.transaction.TransactionInterceptor">
<property name="transactionAttributeSource" ref="transactionAttributeSource" />
<property name="transactionManager" ref="liferayTransactionManager" />
</bean>
<bean id="transactionAttributeSource" class="com.liferay.portal.spring.transaction.AnnotationTransactionAttributeSource" />
有谁知道我怎么能做到这一点?
我在 tomcat 6.0.32 容器中使用 Liferay 6.0 EE sp2。