2012-10-31 7 views
13

In meinem Projekt verwende ich derzeit AspectJ (nicht nur Spring AOP aufgrund einiger Einschränkungen) mit der Weberei zur Kompilierzeit. Um die Entwicklung auf Eclipse zu beschleunigen, möchte ich das Weben zur Ladezeit durchführen. Es gelingt mir, das zu tun, aber mit einer großen Einschränkung: eine Schnittstelle für meinen Dienst zu verwenden, die einige transaktionale Methoden enthielt. Wenn ich den Dienst mit seiner Implementierung anstelle seiner Schnittstelle deklariere, gibt es in der Aufruferklasse keine Weberei und daher wird keine Transaktion unterstützt.Wie konfiguriere ich AspectJ mit Ladezeit Weben ohne Schnittstelle

Also Wenn es von AspectJ unterstützt wird, wie AspectJ mit Load Time Weben ohne Schnittstelle zu konfigurieren?

habe ich ein kleines Projekt, das das Problem zu reproduzieren:

Der folgende Test nicht bestehen.

Der folgende Test erfolgreich sein, wenn:

  • der injizierte Dienst mit seiner Schnittstelle statt deren Umsetzung deklariert wird (dh ersetzen "@Inject MyServiceImpl Service" von "@Inject MyService Service"), gelingt es der Test .

  • Das Weben wird während der Kompilierung ausgeführt (die Konfiguration, POM & Feder Anwendungskontext, ist in diesem Fall offensichtlich anders). Aber mein Ziel ist es, das Weben zur Ladezeit durchzuführen, um jedes Mal, wenn ich eine Java-Datei speichere, eine Webphase zu vermeiden.

  • Spring AOP (tx: annotation-driven mode = "proxy"), das ist eine Proxy-basierte Lösung, wird anstelle von AspectJ verwendet. Aber in diesem Fall haben wir das Problem des Selbstaufrufs festgestellt, d. H. Eine Methode innerhalb des Zielobjekts, die eine andere Methode des Zielobjekts aufruft, führt nicht zur tatsächlichen Transaktion zur Laufzeit, selbst wenn die aufgerufene Methode mit @Transactional markiert ist.

aspectj-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()); 
    } 
} 

aspectj-LTW/src/main/java/mycompany/aspectj_ltw/MyService.java

package mycompany.aspectj_ltw; 

public interface MyService { 

    boolean isExecutedInTransaction(); 

} 

aspectj-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(); 
    } 

} 

aspectj-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> 

aspectj-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> 

aspectj-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 Argumente den Test auszuführen:

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

Antwort

14

Wenn ich mich nicht irre, ist das Problem hier nicht auf AspectJ zurückzuführen, sondern auf die Art, wie die Dinge im präzisen JUnit-Anwendungsfall funktionieren.Wenn Sie Ihren Test ausführen, wird zuerst die Klasse MyServiceImplTest geladen, bevor der Spring-Kontext erstellt wurde (Sie benötigen die Annotationen der Testklasse, um die geeigneten Runner- und Konfigurationsspeicherorte zu erhalten), also bevor ein Spring AOP-Mechanismus genutzt wurde. Das ist zumindest die Erklärung, die ich vor ein paar Monaten hatte, als ich vor genau derselben Situation stand ... Da der Javaagent vom JVM-Startup an da ist, müsste man den Code des Webers genau lesen und verstehen erkläre, warum es hier versagt (habe ich nicht: p).

Also, der MyServiceImplTest Typ, zusammen mit all seinen Member-Typen, die damit geladen sind - das gilt auch für Typen in Methodensignaturen - kann nicht gewebt werden.

um dieses Problem zu umgehen:

  • entweder vermeiden, dass die gewebte Typen in der Testklasse Member und Methoden Signatur (zB mit Hilfe von Schnittstellen wie Sie haben)
  • oder AspectJ Weber zu Ihrem javaagents hinzufügen (zusätzlich zu dem Frühlingsinstrument eins); Wenn ich mich richtig erinnere, sollte Spring in der Lage sein, damit seine AOP-basierten Mechanismen richtig funktionieren:

    -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

Nota: in Ihre META-INF/aop.xml, kann es notwendig sein, um die -Xreweavable Weber-Option hinzuzufügen.

+0

Thank you! Ich denke, das ist eine ziemlich genaue Erklärung dessen, was vor sich geht.Ich bin nicht 100% sicher, denn nach dem, was ich gehört habe Spring zwei Klassenladegeräte und zwei Klassenladekarten - eine Testklasse und ihre Referenzen sollten zuerst in den ersten Lader geladen werden, Anmerkungen analysiert usw., aber dann werden sie verworfen und über AspectJ geladen zur Verfügung gestellt Lader, die das Weben tut ... Theoretisch sollte es Weben ermöglichen, richtig zu passieren, aber irgendwie tut es nicht. Ich werde versuchen, aspectjweaver javaagent und/oder reweavable Optionen hinzuzufügen, um zu sehen, ob es es behebt! – alexandroid

+3

f *** ng .... brilliant. Ich habe gerade nach den frustrierendsten Stunden, die ich nach dem Ratnehmen habe, nach einiger Zeit den größten Atemzug genommen. Für die Aufzeichnung, einschließlich ein 'aspectjweaver' zusammen mit' feder-instrument' als javaagent ermöglichte mir, das Laden-Zeitweben richtig in einer Spring-Boot-Anwendung laufen zu lassen, einschließlich der Junit-Tests – drewmoore

+0

@drewmoore Ich arbeite mit einem Spring-Boot mvc-Anwendung und muss eine Methode von einer anderen Methode innerhalb derselben Controller-Klasse aufrufen, wobei die letztere Methode mit '@ Async' versehen ist. Mir wurde gesagt, dass dies nicht möglich ist, wenn ich den "AspectJ-Proxy-Modus nicht aktiviere und einen Weber anbiete". Ist das ähnlich wie bei dir? –

0

Vor allem, wenn Sie Maven verwenden, setzen Sie Ihre pom.xml:

<dependency> 
     <groupId>org.springframework</groupId> 
     <artifactId>spring-instrument</artifactId> 
     <version>3.1.2.RELEASE</version> 
    </dependency> 
    <dependency> 
     <groupId>org.aspectj</groupId> 
     <artifactId>aspectjweaver</artifactId> 
     <version>1.7</version> 
    </dependency> 

Dann haben Sie hatten Ihren Code mit dem aspectj Compiler zu kompilieren. Dieser Compiler generieren eine aop.xml Datei in META-INF/aop.xml

(Ich verwende Eclipse sts) Danach möchte ich ein JUnit-Test auszuführen. Sie müssen also Ihre VM-Argumente im Konfigurationsfenster für die Eclipse-Ausführung festlegen: -javaagent: $ {ASPECTJ_WEAVER_1.7} \ aspectjweaver-1.7.0.jar -javaagent: $ {SPRING_INSTRUMENT} \ spring-instrument-3.1.2. RELEASE.jar

wo $ {ASPECTJ_WEAVER_1.7} $ {SPRING_INSTRUMENT} sind eine Umgebung var. Verwenden Sie die var-Schaltfläche, um diese Variablen zu erstellen (befindet sich unten rechts im Fenster). Diese vars zielen auf die Ordner ab, in denen sich die aspectjweaver-1.7.0.jar und spring-instrument-3.1.2.RELEASE.jar befinden. Folge dem Assistenten, um das zu machen. Es ist nicht schwer. Achten Sie darauf, dass die vorherigen Java-Agentenzeilen keinen unsichtbaren seltsamen Charakter oder ähnliches haben. Es klingt seltsam, aber ich musste mehrmals die gleiche Zeile umschreiben, bis die Eklipse sagte, dass diese Linie in Ordnung ist.

Dann können Sie Ihren Junit-Test ausführen. Die erste, die Sie sehen können, ist aspectj Laufzeit laden. Später werden Sie eine Federbelastung sehen ... und danach wird Ihr Test laufen, wenn Sie kein Federproblem oder ähnliches haben. Dies ist ein schwerer Prozess.

Ich hoffe, diese Informationen

helfen kann

Grüße