2012-08-13 12 views
6

Natknąłem się na przypadek, w którym proxy AOP utworzone przy użyciu @Cacheable zrywa wtrysk zależności wiosną 3.1.1. Oto mój scenariusz:@Przekładalne przerwy DependencyInjection

Mam interfejs i klasę implementującą ten interfejs przy użyciu @Cacheable w zaimplementowanej metodzie.

przykład interfejs:

public interface ImgService { 
    public byte[] getImage(String name); 
} 

Przykład realizacji:

public class ImgServiceImpl implements ImgService { 

    @Cacheable(cacheName = "someCache") 
    public byte[] getImage(String name){//TODO}; 

    protected String someOtherMethod(){//}; 
} 

również mieć klas testowych JUnit - jednej, która wtryskuje interfejsu i jedną realizację:

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

    @Inject 
    private ImgService; 
} 

i

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

    @Inject 
    private ImgServiceImpl; 
} 

Wstrzyknięcie zależności interfejsu działa dobrze. Jednak, gdy dostanę wstrzykiwanie implementacji w drugiej klasie testowej, otrzymam "Injection of auticedred dependencies failed". Udało mi się to zdebugować i wydaje się, że ClassUtils.isAssignableValue() błędnie porównuje żądany typ z klasą proxy. Jest wywoływana przez DefaultListableBeanFactory. Jeszcze dziwniejsze jest to, że jeśli usunę adnotację @Cacheable z zaimplementowanej metody i dodaję ją do innej chronionej/prywatnej metody, wtrysk zależności znów działa dobrze. Czy to błąd i jakie byłoby właściwe podejście do rozwiązania tej sytuacji?

+0

Oto jeden więcej dobre referencje - http://blog.springsource.org/2012/05/23/understanding-proxy-usage-in-spring/ –

Odpowiedz

3

OK, więc tutaj jest rozwiązanie wpadłem wreszcie.I wdrożone prostą metodę, która próbuje wydobyć obiekt docelowy od pełnomocnika na podstawie jego realizacji klasy org.springframework.aop.framework.Advised:

@SuppressWarnings({"unchecked"}) 
public static <T> T getTargetObject(Object proxy, Class<T> targetClass) { 
    if (AopUtils.isJdkDynamicProxy(proxy)) { 
    try { 
     return (T) ((Advised)proxy).getTargetSource().getTarget(); 
    } catch (Exception e) { 
     return null; 
    } 
    } else { 
    return (T) proxy; 
    } 
} 

Moja implementacja klasy testu teraz wygląda tak:

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

    @Inject 
    private ImgService imgService; 

    private ImgServiceImpl imgServiceImpl; 

    @PostConstruct 
    public void setUp() { 
     imgServiceImpl = getTargetObject(imgService, ImgServiceImpl.class); 
    } 


} 
10

To nie jest błąd, to oczekiwany efekt uboczny stosowania dynamicznych serwerów proxy JDK do implementacji AOP.

Ponieważ wszystkie wywołania metody pamięci podręcznej ImgServiceImpl powinny przechodzić przez dynamiczny proxy typu ImgService, nie ma sposobu na wstrzyknięcie tej zależności do pola typu ImgServiceImpl.

Podczas przenoszenia @Cacheable do private lub protected metoda wtrysku działa, ponieważ @Cacheable nie obowiązywać w tym przypadku - tylko public metody można adviced użyciu AOP proxy oparte.

Powinieneś więc zadeklarować pola, które mają być wstrzyknięte jako ImgService, lub skonfigurować Spring, aby używać serwerów proxy opartych na klasach docelowych zamiast używać proxy-target-class = "true".

Kolejną opcją jest skonfigurowanie Spring do użycia AspectJ-based AOP implementation (wymaga czasu kompilacji lub tkania czasu ładowania).

Ma zastosowanie do wszystkich funkcji opartych na AOP dostarczanych przez Spring (transakcje, bezpieczeństwo, wykonywanie asynchroniczne, pamięć podręczna, niestandardowe aspekty itp.).

Zobacz także:

+0

Dzięki za odpowiedź. Ciągle wydaje mi się dziwne, że metoda ClassUtils nie radzi sobie z tym, ponieważ wydaje się, że zajmuje niewiele więcej niż instrukcja if. – kpentchev

Powiązane problemy