2012-10-31 8 views
13

W moim projekcie używam obecnie AspectJ (nie tylko Spring AOP ze względu na pewne ograniczenia) z tkaniem w czasie kompilacji. W celu przyspieszenia rozwoju Eclipse, chcę zrobić tkactwo w czasie ładowania. Udaje mi się to zrobić, ale z jednym wielkim ograniczeniem: używanie interfejsu dla mojej usługi, który zawierał pewne metody transakcyjne. Jeśli deklaruję usługę z jej implementacją zamiast interfejsu, w klasie wywołującej nie ma tkania, więc nie jest obsługiwana żadna transakcja.Jak skonfigurować AspectJ z czasem ładowania Tkanie bez interfejsu

Więc jeśli jest obsługiwany przez AspectJ, jak skonfigurować AspectJ z Load Time Weaving bez interfejsu?

stworzyłem mały projekt, który odtworzenia problemu:

następujący test nie powiedzie się.

Poniższa próba się uda, jeżeli:

  • wstrzyknięta usługa jest zadeklarowana z interfejsem zamiast jego realizacji (tj zastąpić „usługi @Inject MyServiceImpl” przez „@Inject usługi MojaUsluga”), test się uda .

  • Tkanie wykonywane jest podczas kompilacji (konfiguracja, POM & Kontekst aplikacji wiosennej jest oczywiście inny w tym przypadku). Ale moim celem jest tkanie w czasie ładowania, aby uniknąć fazy tkania za każdym razem, gdy zapisuję plik Java.

  • Spring AOP (tx: tryb sterowany adnotacją = "proxy"), czyli rozwiązanie oparte na proxy, jest używane zamiast AspectJ. Ale w tym przypadku napotkaliśmy problem z samo-wywoływaniem, tj. Metoda w obiekcie docelowym wywołująca inną metodę obiektu docelowego, nie doprowadzi do rzeczywistej transakcji w czasie wykonywania, nawet jeśli wywołana metoda jest oznaczona jako @Transactional.

AspectJ LTW/src/test/Java/mojafirma/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/mojafirma/aspectj_ltw/MyService.java

package mycompany.aspectj_ltw; 

public interface MyService { 

    boolean isExecutedInTransaction(); 

} 

AspectJ LTW/src/main/Java/mojafirma/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 argumenty aby uruchomić test:

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

Odpowiedz

14

Jeśli się nie mylę, problem tutaj nie jest związany z AspectJ, ale raczej z tym, jak rzeczy działają w konkretnym przypadku użycia JUnit.Podczas przeprowadzania testu, klasa MyServiceImplTest jest ładowana najpierw, zanim utworzono kontekst Wiosny (potrzebujesz adnotacji klasy testowej, aby uzyskać odpowiednie lokalizacje biegacza i konfiguracji), a więc zanim zostanie zastosowany mechanizm Spring AOP. To jest przynajmniej wyjaśnienie, które wymyśliłem, gdy kilka miesięcy temu stanąłem przed tą samą sytuacją ... Ponieważ javaagent jest od startu JVM, trzeba dokładnie przeczytać/zrozumieć kod tkacza, aby dokładnie wyjaśnij, dlaczego tutaj się nie udaje (ja nie: p).

Tak więc, typ MyServiceImplTest, wraz z wszystkimi typami jego członków, które są nim ładowane - dotyczy to również typów w sygnaturach - nie mogą być tkane.

Aby obejść ten problem:

  • albo uniknąć stosując tkane typy w podpisie członków i metod klasy testu (np za pomocą interfejsów jak ty)
  • lub dodać AspectJ tkacz do javaagents (oprócz wiosennego instrumentu); z tym, jeśli dobrze pamiętam, Spring powinien być w stanie sprawić, by jego mechanizmy oparte na AOP działały poprawnie:

    -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: w META-INF/aop.xml, może konieczne jest dodanie opcji tkacza -Xreweavable.

+0

dziękuję! Myślę, że to całkiem bliskie wyjaśnienie tego, co się dzieje.Nie jestem w 100% pewny, ponieważ z tego, co słyszałem, Spring używa 2-klasowych ładowarek i 2-klasowych przepustek - klasa testowa i jej referencje powinny być najpierw załadowane do pierwszego programu ładującego, analizowanych adnotacji itp., Ale potem zostają odrzucone i ładowane przez AspectJ pod warunkiem, że ładowarka, która robi tkactwo ... Teoretycznie powinno to pozwolić na prawidłowe tkanie, ale jakoś tak nie jest. Spróbuję dodać Aspirejweaver javaagent i/lub opcje do zmiany, aby sprawdzić, czy to naprawi! – alexandroid

+3

f *** ng .... genialny. Właśnie odetchnąłem z największym oddechem, po kilku frustrujących kilku godzinach, które mam już od jakiegoś czasu po otrzymaniu twojej rady. Dla zapisu, w tym 'aspectjweaver' wraz z' spring-instrument' jako javaagent, umożliwiło mi prawidłowe tasowanie czasu ładowania w aplikacji wiosennych butów, testy jednoczęściowe obejmowały – drewmoore

+0

@drewmoore Pracuję z wiosennym butem Aplikacja mvc i potrzeba wywołania jednej metody z innej metody w obrębie tej samej klasy kontrolera, gdzie ta ostatnia metoda jest opisana przy użyciu '@ Async'. Powiedziano mi, że nie jest to możliwe, chyba że włączę tryb proxy AspectJ i dostarczy tkacz. Czy to jest podobne do tego, co robisz? –

0

Przede wszystkim, jeśli używasz Maven, ustaw 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> 

Potem trzeba było skompilować kod przy użyciu kompilatora AspectJ. Ten kompilator generuje plik aop.xml w pliku META-INF/aop.xml

(Używam Eclipse sts) Po tym chcę przeprowadzić test Junit. Musisz więc ustawić argumenty maszyn wirtualnych w oknie konfiguracji uruchamiania zaćmienia: -javaagent: $ {ASPECTJ_WEAVER_1.7} \ aspectjweaver-1.7.0.jar -javaagent: $ {SPRING_INSTRUMENT} \ spring-instrument-3.1.2. RELEASE.jar

gdzie $ {ASPECTJ_WEAVER_1.7} $ {SPRING_INSTRUMENT} są środowiskiem var. Użyj przycisku var, aby utworzyć te vary (znajduje się w prawym dolnym rogu okna). Te vary są kierowane do folderów, w których znajdują się pliki aspectjweaver-1.7.0.jar i spring-instrument-3.1.2.RELEASE.jar. Postępuj zgodnie z asystentem, aby to zrobić. To nie jest trudne. Należy się upewnić, że poprzednie linie javaagent nie mają żadnych niewidocznych dziwnych znaków lub podobnych. Brzmi to dziwnie, ale musiałem przerobić kilka razy tę samą linię, dopóki eclipse nie powie, że ta linia jest w porządku.

Następnie można uruchomić test Junit. Pierwszym z nich jest ładowanie środowiska wykonawczego aspectj. Później zobaczysz wiosenne ładowanie ... a po tym czasie twój test będzie działał bez żadnych problemów z wiosną lub podobnych. To jest ciężki proces.

Mam nadzieję, że ta informacja może pomóc

odniesieniu

Powiązane problemy