2011-01-16 16 views
29

Rozgrywam pomysł wykorzystania Spring @Configurable i @Autowire do wstawiania obiektów DAO do obiektów domenowych, aby nie potrzebowały bezpośredniej wiedzy o warstwie trwałości.Wiosenne testowanie za pomocą @Configurable

Próbuję podążać za http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-atconfigurable, ale mój kod wydaje się nie mieć żadnego efektu.

Zasadniczo mam:

@Configurable 
public class Artist { 

    @Autowired 
    private ArtistDAO artistDao; 

    public void setArtistDao(ArtistDAO artistDao) { 
     this.artistDao = artistDao; 
    } 

    public void save() { 
     artistDao.save(this); 
    } 

} 

I:

public interface ArtistDAO { 

    public void save(Artist artist); 

} 

i

@Component 
public class ArtistDAOImpl implements ArtistDAO { 

    @Override 
    public void save(Artist artist) { 
     System.out.println("saving"); 
    } 

} 

W aplikacji context.xml, mam:

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springsource.org/dtd/spring-beans-2.0.dtd"> 
<beans> 

    <bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator" /> 
    <bean class="org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect" factory-method="aspectOf"/> 

</beans> 

Skanowanie i inicjalizacja ścieżki klas odbywa się za pomocą modułu sprężyn do Play! ramy, chociaż działają inne autoczyred bean, więc jestem prawie pewien, że to nie jest podstawowa przyczyna. Używam Spring 3.0.5.

W innym kodem (wewnątrz metody w fasoli, który jest wtryskiwany do mojego kontrolera przy użyciu Spring, w rzeczywistości), to robię:

Artist artist = new Artist(); 
artist.save(); 

To daje mi NullPointerException próbuje uzyskać dostęp do artistDao w Artysty .zapisać().

Każdy pomysł, co robię źle?

Martin

+0

związane;) http://techblog.bozho.net/?p=180 – Bozho

+0

można używać '@ value' adnotacji w pliku' @ Configurable' oznaczone klasy artysty? – NightWolf

Odpowiedz

1

Być pomocą @Repository adnotacji dla DAO to zrobi.

+0

Zgadzam się. Powinien używać @Repository zamiast: – chris

+2

Jak może być powiązany z '@ Configurable'? – axtavt

+5

Czy możesz wyjaśnić, dlaczego to miałoby znaczenie? – Paul

7

Musisz włączyć tkanie czasu ładowania (lub innego rodzaju tkania), aby użyć @Configurable. Upewnij się, że włączasz go poprawnie, zgodnie z opisem w 7.8.4 Load-time weaving with AspectJ in the Spring Framework.

+1

To jest złe. Nie potrzebujesz LTW do obsługi @Configurable. Spring Roo robi to, czego chce i nie korzysta z LTW. Najprawdopodobniej jego problem polega na tym, że nie kompiluje kodu za pomocą kompilatora AspectJ i Spring Aspects jar. –

+4

@AdamGent Powiedział LTW (lub inne rodzaje tkania). . . Aby utworzyć adnotację konfigurowalną, potrzebujesz albo tkania czasu obciążenia, albo kompilowania czasu. –

+0

@JasperBlues masz rację, że LTW powinno działać. Nigdy nie mogłem go uruchomić w moim środowisku i pomyślałem, że przeczytałem w * AspectJ w akcji *, że konstruowanie porad nie było możliwe przy LTW. –

4

Najpierw włącz rejestrowanie debugowania Spring. Używam Log4j, aby to zrobić. Utworzyłem rejestratora jak tak (z konfiguracji xml Log4j więc mogę używać RollingFileAppender):

<log4j:configuration> 
    <appender name="roll" class="org.apache.log4j.rolling.RollingFileAppender"> 
    blah blah configuration blah blah 
    </appender> 
    <logger name="org.springframework"> 
    <level value="debug" /> 
    <appender-ref ref="roll" /> 
    </logger> 
</log4j:configuration> 

To pozwoli Ci zobaczyć, co wiosna robi i kiedy.

Po drugie, masz autopsję ArtistDAO, ale nie widzę, gdzie masz fasolę o nazwie ArtistDAO. Twój komponent komponentu DAO będzie domyślnie nazwany "artistDaoImpl". Spróbuj zmienić @Component do @Component("artistDao") i stosowania @Autowired do seter Zamiast:

private ArtistDAO artistDao; 

@Autowired 
public void setArtistDao(ArtistDAO artistDao) 
{ 
    this.artistDao = artistDao; 
} 
3

Trzeba tylko patrzeć jak Spring Roo robi to, ponieważ robi dokładnie to, co chcesz robić.

Istnieje wiele rzeczy, które mogą powodować NPE od posiadania, ale przez większość czasu ma do czynienia z nie kompilowanie prawidłowo z AspectJ kompilatora i nie mając Wiosna Aspekty jar w ścieżce lib AspectJ (ta jest inna niż twoja ścieżka klas).

Najpierw spróbuj go uruchomić z Mavenem i wtyczką kompilatora AspectJ. Właśnie dlatego polecam Spring Roo, ponieważ wygeneruje plik POM z prawidłową konfiguracją.

Znalazłem @Configurable naprawdę nie działa z LTW (pomimo jednej z odpowiedzi). Aby funkcja @Configurable działała, potrzebujesz kompilacji czasu, ponieważ porady pojawiają się w czasie budowy obiektu (porady konstruktora nie można uzyskać przy pomocy sprężyn sprężynowych Springs).

+0

Jakie problemy wystąpiły podczas tkania w czasie ładowania? Użyłem tego całkiem pomyślnie. Używam LTW do testów integracyjnych i do tasowania podczas kompilacji w celu wdrożenia. . W ten sposób moje raporty dotyczące zasięgu testów są ładnie wyświetlane, ale moje wdrożenie jest solidne. –

+0

Ponieważ '@ Configurable' pozwala na użycie normalnych konstruktorów Java do utworzenia instancji obiektu. Porada dotyczy konstruktora obiektów. ** Pomyślałem, że możesz zastosować tylko porady konstruktora z czasem kompilacji **, ale wygląda na to, że możesz to zrobić również przy pomocy LTW. Więc masz rację. Czego nie możesz zrobić (i dlatego się myliłem) to tworzenie ITDów AspectJ z LTW. –

+0

Ahhh, rozumiem. . Tego nie wiedziałem. –

1

try: @Configurable (autowire = Autowire.BY_TYPE). Automatyczne wyłączanie domyślnie wyłączone: <

1

Miałem podobny problem, który rozwiązałem dzisiaj. Ważne jest, aby włączyć tkanie czasu ładowania i upewnić się, że załadowane są odpowiednie klasy aspectj. W swojej pom.xml trzeba dodać aspectjweaver artifact:

... 
<dependency> 
    <groupId>org.aspectj</groupId> 
    <artifactId>aspectjweaver</artifactId> 
    <version>1.6.12</version> 
</dependency> 
.... 

Można zmienić wersję, jeśli trzeba. Potem pójdę trasę xsd w was aplikacji context.xml zamiast trasie DTD:

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

    <!--Scans the classpath for annotated components @Component, @Repository, @Service, and @Controller --> 
    <context:component-scan base-package="your.base.package"/> 
    <!--Activates @Required, @Autowired, @PostConstruct, @PreDestroy and @Resource --> 
    <context:annotation-config/> 
    <!--This switches on the load-time weaving for @Configurable annotated classes --> 
    <context:load-time-weaver/> 

</beans> 
6

miałem ten problem z Tomcat 7 za pomocą LTW próbuje autowire fasolę do moich klas domeny.

Było kilka aktualizacji dokumentu dla wersji 3.2.x pod numerem http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/aop.html#aop-configurable-container, które ujawniły, że można użyć @EnableSpringConfigured zamiast konfiguracji xml.

Więc mam następującą adnotację na mojego obiektu domeny:

@Configurable(preConstruction=true,dependencyCheck=true,autowire=Autowire.BY_TYPE) 
@EnableSpringConfigured 

@EnableSpringConfigured to podstawniki, dla

<context:spring-configured /> 

i nie zapomnij dodać go do pliku xml kontekstowego:

<context:load-time-weaver weaver-class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver" aspectj-weaving="on"/> 

Oczywiście musiałem skonfigurować Tomcat, aby najpierw rozpocząć tkanie czasu ładowania.

też wpadłem na bug w 3.2.0 (null pointer), więc potrzebne do uaktualnienia do wiosny 3.2.1 (https://jira.springsource.org/browse/SPR-10108)

Wszystko jest dobrze teraz!

0

Należy również sprawdzić, czy wersja programu AspectJ jest aktualna. Zmarnowałem kilka godzin, próbując to zrobić, a przyczyną była stara wersja pliku Aspectjweaver.jar. Zaktualizowałem do wersji 1.7.2 i wszystko działało jak czar.

3

Miałem ten sam problem i nigdy nie udało mi się uzyskać kodu działającego z @Configurable i @Autowired. W końcu zdecydowałem się napisać osobiście aspekt, który obsłużyłby adnotacje @Configurable i @Autowired. Oto kod:

import java.lang.annotation.Annotation; 
import java.lang.reflect.Field; 

import org.apache.log4j.Logger; 
import org.aspectj.lang.JoinPoint; 
import org.aspectj.lang.annotation.Aspect; 
import org.aspectj.lang.annotation.Before; 
import org.aspectj.lang.annotation.Pointcut; 
import org.springframework.beans.BeansException; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.context.ApplicationContext; 
import org.springframework.context.ApplicationContextAware; 

@SuppressWarnings("rawtypes") 
@Aspect 
public class AutoInjectDependecyAspect implements ApplicationContextAware { 
    private static final Logger LOGGER = Logger.getLogger(AutoInjectDependecyAspect.class); 

    private ApplicationContext applicationContext = null; 

    @Pointcut("execution( (@org.springframework.beans.factory.annotation.Configurable *).new())") 
    public void constructor() { 
    } 

    @Before("constructor()") 
    public void injectAutoWiredFields(JoinPoint aPoint) { 
     Class theClass = aPoint.getTarget().getClass(); 
     try{ 
      Field[] theFields = theClass.getDeclaredFields(); 
      for (Field thefield : theFields) { 
       for (Annotation theAnnotation : thefield.getAnnotations()) { 
        if (theAnnotation instanceof Autowired) { 
         // found a field annotated with 'AutoWired' 
         if (!thefield.isAccessible()) { 
          thefield.setAccessible(true); 
         } 

         Object theBean = applicationContext.getBean(thefield.getType()); 
         if (theBean != null) { 
          thefield.set(aPoint.getTarget(), theBean); 
         } 
        } 
       } 
      } 
     } catch (Exception e) { 
      LOGGER.error("An error occured while trying to inject bean on mapper '" + aPoint.getTarget().getClass() + "'", e); 
     } 

    } 

    @Override 
    public void setApplicationContext(ApplicationContext aApplicationContext) throws BeansException { 
     applicationContext = aApplicationContext; 
    } 

} 

Następny w kontekście wiosennej define aspekt tak że springcontext będą wstrzykiwane do aspektu

<bean class="[package_name].AutoInjectDependecyAspect" factory-method="aspectOf"/> 
Powiązane problemy