Spring+HibernateEntityManager(AspectJ AOP with Load-Time-Weaver編)
今回は、Spring+HibernateEntityManager(@Required編)の続きです。
トランザクションがかからない!?
現在は、ServiceImpl#registerUser(User) メソッドに、@Transactional アノテーションを用いてトランザクションをかけています。
@Transactional public void registerUser(User<Integer> user) { getEntityManager().persist(user); }
ここで、このメソッドにバリデーションをかけるために、メソッドを分割しようとおもいます。
ということで、現在1つのメソッドを、下記の3つのメソッドに分割してみます。
- 元の registerUser メソッドを、下記の2メソッドを呼び出すようにして、@Transaction アノテーションを削除。
- User 登録の可否をチェックするメソッドを作成。
- 実際に User を登録するメソッドを作成し、@Transaction アノテーションを設定。
/** * {@inheritDoc} */ @Override public void registerUser(User<Integer> user) { if (isRegisterableUser(user)) { doRegisterUser(user); } } /** * User が登録可能かどうかを返します。 * * @param user * User * @return User が登録可能な場合は true */ protected boolean isRegisterableUser(User<Integer> user) { return true; } /** * User を登録します。 * * @param user * User */ @Transactional protected void doRegisterUser(User<Integer> user) { getEntityManager().persist(user); }
では、実行!
おや? トランザクションがかかりません o...rz
プロキシベースのAOPの限界
なぜ、トランザクションがかからなかったのでしょうか?
それは、プロキシをベースとしたAOPの限界を超えたからです。
以前は、Main が持つ UserService の参照は、実は UserServiceImpl をラップしたプロキシでした。そのため、Main が UserService#registerUser(User) を呼び出す場合、プロキシを経由して処理が行われました。そして、プロキシを経由する際に、トランザクションを開始する処理が実行されていました。
しかし今回は、 UserServiceImpl#registerUser(User) には @Transactional アノテーションがありません。そのため、プロキシを経由する際には、トランザクションに関する処理は行われません。そして、UserServiceImpl#registerUser(User) から UserServiceImpl#doRegisterUser(User) が呼ばれるわけですが、同一インスタンス内での呼び出しのため、プロキシを経由しません。そうすると、トランザクションを開始する処理が行われません。
※詳細は SpringFramework のマニュアルの 6.6.1. Understanding AOP proxies で説明されているので、ぜひご一読を。
AspectJ があるじゃないか
プロキシがダメなら、アスペクトがあるじゃないか。というわけで、AspectJ を使ってみることにします。
load-time-weaver
beans.xml に、load time weaver の設定をします。
<!-- LTW enables AOP not "Spring AOP" but "Aspectj AOP" --> <context:load-time-weaver/>
この設定をするだけでは、以下のようなエラーが出てしまいますので、次に javaagent の設定を行います。
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'loadTimeWeaver': Initialization of bean failed; nested exception is java.lang.IllegalStateException: ClassLoader [sun.misc.Launcher$AppClassLoader] does NOT provide an 'addTransformer(ClassFileTransformer)' method. Specify a custom LoadTimeWeaver or start your Java virtual machine with Spring's agent: -javaagent:spring-agent.jar Caused by: java.lang.IllegalStateException: ClassLoader [sun.misc.Launcher$AppClassLoader] does NOT provide an 'addTransformer(ClassFileTransformer)' method. Specify a custom LoadTimeWeaver or start your Java virtual machine with Spring's agent: -javaagent:spring-agent.jar
javaagentの設定
まずは、spring-agent.jar を用意します。そして、javaの起動オプションで、javaagentを指定します。
以下、VMオプションの例。
-javaagent:/spring-agent-2.5.4.jar
annotation-driven
beans.xml の、annotation-driven の設定を変更します。
以前は、以下のように設定していました。
<!-- for @Transactional annotations --> <tx:annotation-driven transaction-manager="txManager"/>
今回は、AspectJ を使用するために、設定を以下のように変更します。
<!-- Enables @Transactional annotations with "Aspectj AOP" --> <tx:annotation-driven transaction-manager="txManager" mode="aspectj"/>
jarの追加
上記のように設定しても、以下のように怒られてしまうので、spring-aspects.jar を追加します。
Exception in thread "main" org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [org.springframework.transaction.aspectj.AnnotationTransactionAspect] for bean with name 'org.springframework.transaction.config.internalTransactionAspect' defined in null; nested exception is java.lang.ClassNotFoundException: org.springframework.transaction.aspectj.AnnotationTransactionAspect Caused by: java.lang.ClassNotFoundException: org.springframework.transaction.aspectj.AnnotationTransactionAspect
pom.xml への dependency の追加。
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>2.5.4</version> </dependency>
AspectJ AOP with Load-Time-Weaver 版の実行!
今度は、下記のように、DBに対して insert が走りました。
トランザクションの設定が正しく行われたようです。(`・ω・´)
SET autocommit=0 insert into User (name) values ('Kumapiyo') commit SET autocommit=1
links
第1回 Spring+HibernateEntityManager(HibernateEntityManager単体編)
第2回 Spring+HibernateEntityManager(とりあえずSpring編)
第3回 Spring+HibernateEntityManager(宣言的トランザクション編)
第4回 Spring+HibernateEntityManager(Spring+DDDっぽく編)
第5回 Spring+HibernateEntityManager(Spring+DDDっぽく編 その2)
第6回 Spring+HibernateEntityManager(Spring+DDDっぽく編 その3)
第7回 Spring+HibernateEntityManager(Spring+DDDっぽく編 その4)
第8回 Spring+HibernateEntityManager(@Transactionalアノテーション編)
第9回 Spring+HibernateEntityManager(@Required編)
第10回 Spring+HibernateEntityManager(XMLからの外部リソース参照編)
第11回 Spring+HibernateEntityManager(AspectJ AOP with Load-Time-Weaver編)
第12回 Spring+HibernateEntityManager(DBCPのvalidationQuery編)
第13回 Spring+HibernateEntityManager(@Resource編)
第14回 Spring+HibernateEntityManager(コンポーネント自動検出 with アノテーション編)
第15回 Spring+HibernateEntityManager(コンポーネント自動検出 without アノテーション編)