2014年5月31日土曜日

SpringでprivateメソッドにもTransactionalアノテーションを適用させる

SpringでprivateメソッドにもTransactionalアノテーションを適用させるにはAspectJを使う必要がある。
以下設定例。

 ■XML
  <tx:annotation-driven mode="aspectj"/>
  <context:annotation-config/>
  <context:load-time-weaver/>

■VM arguments
-javaagent:/path/spring-instrument-4.0.5.RELEASE.jar 

■必要なJAR(以下はcradleの例)
compile 'org.springframework:spring-core:4.0.5.RELEASE'
compile 'org.springframework:spring-context:4.0.5.RELEASE'
compile 'org.springframework:spring-jdbc:4.0.5.RELEASE'
compile 'org.springframework:spring-aspects:4.0.5.RELEASE'
compile 'org.springframework:spring-instrument:4.0.5.RELEASE'
compile 'org.aspectj:aspectjrt:1.8.0'
compile 'org.aspectj:aspectjweaver:1.8.0'

Springで自身のクラスを注入する

Springで以下のように自身のクラスを注入すると実行時に例外が発生する。
@Service
public class SpringBean {

    @Autowired
    private SpringBean self;

}
以下のように@Autowiredでなく@Resource使うことで回避できる。 nameをつけるのがポイント。
@Service
public class SpringBean {

    @Resource(name = "springBean")
    private SpringBean self;

}

Spring+Mirage+JTA(Atomikos)の例

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation=" http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--
Atomikos3.9.3でAtomikosNonXADataSourceBeanを使用するとメモリリークしているようでOutOfMemoryErrorが発生したため非推奨
  <bean id="dataSource" class="com.atomikos.jdbc.nonxa.AtomikosNonXADataSourceBean" init-method="init" destroy-method="close">
  <property name="uniqueResourceName" value="dataSource" />
  <property name="driverClassName" value="com.mysql.jdbc.Driver" />
  <property name="url" value="jdbc:mysql://localhost:3306/test1" />
  <property name="user" value="root" />
  <property name="password" value="password" />
  </bean>
-->
  <bean id="dataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean"
  init-method="init" destroy-method="close">
  <property name="uniqueResourceName" value="dataSource" />
  <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"/>
  <property name="xaProperties">
  <props>
    <prop key="user">root</prop>
    <prop key="password">password</prop>
    <prop key="url">jdbc:mysql://localhost:3306/test1</prop>
  </props>
  </property>
  </bean>
  <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close">
  <property name="forceShutdown" value="false" />
  </bean>
  <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.J2eeUserTransaction">
  <property name="transactionTimeout" value="300" />
  </bean>
  <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager" depends-on="atomikosTransactionManager,atomikosUserTransaction">
  <property name="transactionManager" ref="atomikosTransactionManager" />
  <property name="userTransaction" ref="atomikosUserTransaction" />
  <property name="allowCustomIsolationLevels" value="true" />
  </bean>
  <bean id="connectionProvider" class="jp.sf.amateras.mirage.provider.DataSourceConnectionProvider">
  <property name="dataSource" ref="dataSource" />
  </bean>
  <bean id="dialect" class="jp.sf.amateras.mirage.dialect.MySQLDialect"/>
  <bean id="sqlManager" class="jp.sf.amateras.mirage.SqlManagerImpl">
  <property name="connectionProvider" ref="connectionProvider" />
  <property name="dialect" ref="dialect" />
  </bean>
</beans>