0

我有一个名为 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。

4

1 回答 1

0

Try to move the @transactional annotation on to the interface (not on the method of the interface)

Liferay wraps transactions over beans which names end with Service (depends on the bean name not the class name).

Also the problem could be in placement of the code. Where do you define your bean? I suppose in extplugin? If you liked to extend Liferay transaction in your own portlet without service builder, it can get tricky. However it should be possible to share Liferay spring context in 6.1.

于 2013-03-05T20:42:28.887 回答