2012-10-31 15 views
13

Nel mio progetto, attualmente uso AspectJ (non solo Spring AOP a causa di alcune limitazioni) con la tessitura al momento della compilazione. Per accelerare lo sviluppo su Eclipse, voglio fare la tessitura al momento del caricamento. Riusciamo a farlo, ma con uno dei principali limiti: usare un'interfaccia per il mio servizio che contenesse alcuni metodi transazionali. Se dichiaro il servizio con la sua implementazione anziché la sua interfaccia, nella classe chiamante, non c'è tessitura e quindi nessuna transazione supportata.Come configurare AspectJ con Load Time Weaving senza interfaccia

Quindi se è supportato da AspectJ, come configurare AspectJ con Load Time Weaving senza interfaccia?

ho creato un piccolo progetto che riprodurre il problema:

il seguente test sicuro.

Il seguente test riesce se:

  • il servizio iniettato viene dichiarata con la sua interfaccia invece della sua applicazione (cioè sostituire "service @Inject MyServiceImpl" con "servizio MyService @Inject"), il test riesce .

  • la tessitura viene eseguita durante la compilazione (la configurazione, POM & contesto applicazione Spring, è ovviamente diverso in questo caso). Ma il mio obiettivo è fare la tessitura al momento del caricamento per evitare una fase di tessitura ogni volta che salvi un file Java.

  • Spring AOP (tx: modalità basata su annotazione = "proxy"), che è una soluzione basata su proxy, viene utilizzata al posto di AspectJ. Ma in questo caso, abbiamo riscontrato il problema del richiamo automatico, vale a dire un metodo all'interno dell'oggetto target che chiama un altro metodo dell'oggetto target, non porterà a una transazione effettiva in fase di esecuzione anche se il metodo richiamato è contrassegnato con @Transactional.

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/risorse/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/risorse/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> 

argomenti VM per eseguire il test:

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

risposta

14

Se non sbaglio, il problema qui non è dovuto a AspectJ, ma piuttosto al modo in cui le cose funzionano nel preciso caso d'uso di JUnit.Quando si esegue il test, viene caricata prima la classe MyServiceImplTest, prima che venga creato il contesto Spring (sono necessarie le annotazioni della classe di test per ottenere il percorso appropriato per i corridori e la configurazione), quindi prima di sfruttare qualsiasi meccanismo Spring AOP. Questa è, almeno, la spiegazione che mi è venuta in mente quando ho affrontato la stessa situazione qualche mese fa ... Poiché javaagent è presente all'avvio di JVM, è necessario leggere e comprendere a fondo il codice del tessitore spiega perché fallisce qui (non l'ho fatto: p).

Ad ogni modo, il tipo MyServiceImplTest, insieme a tutti i tipi di membri, che vengono caricati con esso, vale anche per i tipi di firme dei metodi, non può essere intessuto.

Per risolvere questo:

  • evitare di utilizzare i tipi di tessuti nel membri della classe di test e metodi di firma (ad esempio utilizzando le interfacce come avete fatto)
  • o aggiungere l'AspectJ Weaver per i tuoi javaagents (oltre allo strumento a molla); con questo, se ricordo bene, Spring dovrebbe essere in grado di far funzionare correttamente i suoi meccanismi basati su 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

Nota: nella vostra META-INF/aop.xml, può essere necessario aggiungere l'opzione tessitore -Xreweavable.

+0

grazie! Penso che questa sia una spiegazione abbastanza vicina di quello che sta succedendo.Non sono sicuro al 100% perché da quello che ho sentito Spring usa 2 caricatori di classe e 2 passaggi di caricamento di classe - una classe di test e i suoi riferimenti dovrebbero essere caricati per la prima volta nel caricatore, annotazioni analizzate ecc., Ma poi vengono scartati e caricati tramite AspectJ fornito caricatore che fa la tessitura ... In teoria dovrebbe permettere che la tessitura avvenga correttamente ma in qualche modo no. Proverò ad aggiungere aspectjweaver javaagent e/o opzioni modificabili per vedere se lo risolve! – alexandroid

+3

f *** ng .... geniale. ho appena fatto il respiro più grande, dopo le poche ore più frustranti, che ho avuto dopo un po 'di tempo dopo aver seguito il tuo consiglio. Per la cronaca, incluso un 'aspectjweaver' insieme a' spring-instrument' come javaagent mi ha permesso di ottenere la tessitura del tempo di caricamento correttamente in un'applicazione di avvio a molla, i test di junit inclusi – drewmoore

+0

@drewmoore Sto lavorando con uno spring-boot applicazione mvc e necessità di richiamare un metodo da un altro metodo all'interno della stessa classe controller, dove quest'ultimo metodo è annotato con '@ Async'. Mi è stato detto che questo non è possibile a meno che non attivi la modalità proxy di AspectJ e fornisca un weaver'. È simile a quello che stai facendo? –

0

Prima di tutto, se si utilizza Maven, impostare il 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> 

Poi hanno dovuto compilare il codice utilizzando il compilatore AspectJ. Questo compilatore genera un file aop.xml in META-INF/aop.xml

(sto usando STS Eclipse) Dopo di che, voglio eseguire un test JUnit. Quindi devi impostare gli argomenti della tua macchina virtuale nella finestra di configurazione dell'analisi di eclissi: -javaagent: $ {ASPECTJ_WEAVER_1.7} \ aspectjweaver-1.7.0.jar -javaagent: $ {SPRING_INSTRUMENT} \ spring-instrument-3.1.2. RELEASE.jar

dove $ {ASPECTJ_WEAVER_1.7} $ {SPRING_INSTRUMENT} sono var di ambiente. Usa il pulsante var per creare questi oggetti (si trova in basso a destra della finestra). Questi oggetti sono indirizzati alle cartelle in cui si trovano aspectjweaver-1.7.0.jar e spring-instrument-3.1.2.RELEASE.jar. Segui l'assistente per farlo. Non è difficile. Fare attenzione che le linee javaagent precedenti non abbiano alcun carattere strano invisibile o simile. È strano, ma ho dovuto riscrivere più volte la stessa riga fino a quando Eclipse ha detto che questa linea va bene.

Quindi, è possibile eseguire il test di Junit. Il primo che è possibile vedere è il caricamento del runtime aspectj. Più avanti vedrai il caricamento a molla ... e dopo di che il test verrà eseguito non avrà alcun problema di primavera o simili. Questo è un processo pesante.

Spero che questa informazione può aiutare a

saluti