14

私のプロジェクトでは、現在 AspectJ (いくつかの制限により Spring AOP だけでなく) をコンパイル時のウィービングで使用しています。Eclipse での開発を高速化するために、Load Time でウィービングを行いたいと考えています。私はそれを成功させましたが、大きな制約が 1 つあります。いくつかのトランザクション メソッドを含むサービスのインターフェイスを使用することです。インターフェイスではなく実装を使用してサービスを宣言すると、呼び出し元クラスでウィービングが行われないため、トランザクションはサポートされません。

AspectJでサポートされている場合、インターフェイスなしで Load Time Weaving を使用して AspectJ を構成するにはどうすればよいですか?

問題を再現する小さなプロジェクトを作成しました。

次のテストは失敗します。

次のテストは、次の場合に成功します。

  • 注入されたサービスは、その実装の代わりにそのインターフェースで宣言されます (つまり、「@Inject MyServiceImpl サービス」を「@Inject MyService サービス」に置き換えます)、テストは成功します。

  • ウィービングはコンパイル中に実行されます (この場合、構成、POM および Spring アプリケーションのコンテキストは明らかに異なります)。しかし、私の目標は、Java ファイルを保存するたびにウィービング フェーズを回避するために、読み込み時にウィービングを行うことです。

  • AspectJ の代わりに、プロキシベースのソリューションである Spring AOP (tx:annotation-driven mode="proxy") が使用されます。ただし、この場合、自己呼び出しの問題が発生しました。つまり、ターゲット オブジェクト内のメソッドがターゲット オブジェクトの他のメソッドを呼び出している場合、呼び出されたメソッドが @Transactional でマークされていても、実行時に実際のトランザクションにつながりません。

spectj-ltw/src/test/java/mycompany/aspectj_ltw/MyServiceImplTest.java

package mycompany.aspectj_ltw;

import static junit.framework.Assert.assertTrue;

import javax.inject.Inject;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:/META-INF/spring/applicationContext.xml" })
public class MyServiceImplTest {

    @Inject
    MyServiceImpl service;

    @Test
    public void shouldBeExecutedInTransaction() {
        assertTrue(this.service.isExecutedInTransaction());
    }
}

spectj-ltw/src/main/java/mycompany/aspectj_ltw/MyService.java

package mycompany.aspectj_ltw;

public interface MyService {

    boolean isExecutedInTransaction();

}

spectj-ltw/src/main/java/mycompany/aspectj_ltw/MyServiceImpl.java

package mycompany.aspectj_ltw;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationManager;

@Service
public class MyServiceImpl implements MyService {

    @Transactional
    public boolean isExecutedInTransaction() {
        return TransactionSynchronizationManager.isActualTransactionActive();
    }

}

spectj-ltw/src/test/resources/META-INF/applicationContext.xml

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd   http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd   http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd   http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd   http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

    <context:component-scan base-package="mycompany.aspectj_ltw" />

    <context:load-time-weaver aspectj-weaving="on" />
    <aop:config proxy-target-class="true"/>
    <aop:aspectj-autoproxy proxy-target-class="true"/>
    <tx:annotation-driven mode="aspectj"
        transaction-manager="transactionManager" proxy-target-class="true" />

    <bean class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close" id="dataSource">
        <property name="driverClassName" value="org.h2.Driver" />
        <property name="url" value="jdbc:h2:mem:mydb" />
        <property name="username" value="sa" />
        <property name="password" value="" />
    </bean>
    <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
        id="transactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>
</beans>

spectj-ltw/src/test/resources/META-INF/aop.xml

<!DOCTYPE aspectj PUBLIC
        "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
  <weaver options="-showWeaveInfo -debug -verbose -XmessageHandlerClass:org.springframework.aop.aspectj.AspectJWeaverMessageHandler">
        <include within="mycompany.aspectj_ltw..*"/>
  </weaver>
</aspectj>

spectj-ltw\pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>mycompany</groupId>
    <artifactId>aspectj-ltw</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>aspectj-ltw</name>

    <properties>
        <spring.version>3.0.5.RELEASE</spring.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.8.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.7.0</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.7.0</version>
        </dependency>
        <dependency>
            <groupId>javax.inject</groupId>
            <artifactId>javax.inject</artifactId>
            <version>1</version>
        </dependency>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib-nodep</artifactId>
            <version>2.2</version>
        </dependency>
        <dependency>
            <groupId>commons-dbcp</groupId>
            <artifactId>commons-dbcp</artifactId>
            <version>1.4</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.2.143</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>0.9.24</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>0.9.24</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>log4j-over-slf4j</artifactId>
            <version>1.6.1</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <forkMode>always</forkMode>
                    <argLine>
                        -javaagent:C:/maven-2_local_repo/org/springframework/spring-instrument/3.0.5.RELEASE/spring-instrument-3.0.5.RELEASE.jar
                    </argLine>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

テストを実行するための VM 引数:

-javaagent:C:/maven-2_local_repo/org/springframework/spring-instrument/3.0.5.RELEASE/spring-instrument-3.0.5.RELEASE.jar
4

2 に答える 2

16

私が間違っていなければ、ここでの問題は AspectJ によるものではなく、正確な JUnit ユース ケースでの動作方法によるものです。テストを実行するとMyServiceImplTest、Spring コンテキストが作成される前にクラスが最初にロードされます (適切なランナーと構成の場所を取得するには、テスト クラスの注釈が必要です)。つまり、Spring AOP メカニズムが利用される前です。つまり、少なくとも、数か月前にまったく同じ状況に直面したときに私が思いついた説明です... javaagent は JVM の起動時から存在するため、ウィーバーのコードを完全に読んで理解する必要があります。ここで失敗する理由を説明してください(私は知りませんでした:p)。

とにかく、MyServiceImplTest型は、それと共にロードされるすべてのメンバーの型と一緒に (これはメソッド シグネチャの型にも当てはまります)、織り込むことはできません。

これを回避するには:

  • テストクラスのメンバーとメソッドの署名で織り込まれた型を使用しないようにします(たとえば、あなたがしたようにインターフェースを使用します)
  • または、AspectJ ウィーバーを javaagents に追加します (spring-instrument に加えて)。これで、私の記憶が正しければ、Spring は AOP ベースのメカニズムを適切に機能させることができるはずです。

    -javaagent:/maven-2_local_repo/org/aspectj/aspectjweaver/1.7.0/aspectjweaver-1.7.0.jar -javaagent:/maven-2_local_repo/org/springframework/spring-instrument/3.0.5.RELEASE/spring-instrument -3.0.5.RELEASE.jar

注: では、 weaver オプションMETA-INF/aop.xmlを追加する必要がある場合があります。-Xreweavable

于 2013-03-04T14:34:58.910 に答える