Przykładowy konfig Spring'a z cykliczną zależnością (/WEB-INF/application-context.xml):
<bean id="userService" class="pl.codeservice.service.UserService" >
<property name="studioService" ref="studioService" />
</bean>
<bean id="studioService" class="pl.codeservice.service.StudioService" >
<property name="userService" ref="userService" />
</bean>
Zalecane jest pozbycie się cykliczności jako złej praktyki bo:
-zwiększa stopień skomplikowania konfiguracji (OOP principles: loose coupling)
-powoduje problemy architektoniczne: który komponent ma być zniszczony jako pierwszy, istnienie częsciowo zainicjowanych ziaren, itd.
-powiązane komponenty muszą być dystrybuowane w tym samym module/JARze
-trudniej testować
-itd.
O ile generalnie zgadzam sie z powyższym, bywają sytuacje awaryjne kiedy nie ma innego wyjścia. Nie ma czasu i pieniędzy na zmianę architektury a kopiowanie kodu nigdy nie jest dobrym pomysłem.
Rozwiązaniem jest implementacja interfejsu BeanPostProcessor przez jeden z komponentów i przechwycenie referencji do zależnego bean'a w metodzie postProcessAfterInitialization(). Trzeba pamiętać o zwróceniu w obu metodach instancji przekazanego w argumencie ziarna.
public class StudioService implements BeanPostProcessor {
private UserService userService;
//...
public Object postProcessAfterInitialization(Object bean, String name) throws BeansException {
if("userService".equals(name)){
userService = (UserService)bean;
}
return bean;
}
public Object postProcessBeforeInitialization(Object bean, String name) throws BeansException {
return bean;
}
}
Jeśli komponent będzie występował jako cross-reference więcej niż raz warto napisać, skonfigurować i używać w jego miejsce proxy - obejście zawsze będzie tylko w jednym miejscu.
Linki:
-Spring Dynamic Modules Reference Guide, 5.3.1. Listener and cyclic dependencies
-The Spring Framework - Reference Documentation, 3.7. Container extension points
-architecturerules.org