2015-04-27 15 views
5

Mam taką sytuację. Nie widzę żadnych błędów, ale nie otrzymuję wyników.Obsługa zależności cyklicznej w CDI

@ApplicationScoped 
public class A { 

    private B b; 


    @Inject 
    public A(B b) { 
     this.b = b; 
    } 
} 

@Singleton 
public class B { 

    private A a; 


    @Inject 
    public B(A a) { 
     this.a = a; 
    } 
} 

Czy ten rodzaj wtrysku zależność jest źle?

Czy ktoś mi w tym pomoże?

+0

Musisz zmienić kod, przenieść zależny kod z tych klas A i B i utworzyć klasę C. –

+0

@ André. Dziękuję za odpowiedź. Czy możesz powiedzieć, jak to A i B wywołane z C – Patan

+0

CDI nie może obsłużyć zależności okrężnej, EJB może – maress

Odpowiedz

3

Chciałbym uniknąć tej zależności cyklicznej, jest kilka powodów, aby to zrobić.

Skomentuj this article

niechlujny konstruktora jest znakiem. Ostrzega mnie, że moja klasa staje się monolitem, który jest dźwignikiem wszystkich zawodów i mistrzem nikogo. Innymi słowy, niechlujny konstruktor jest naprawdę dobrą rzeczą. Jeśli czuję, że konstruktor klasy jest zbyt brudny, wiem, że nadszedł czas, aby coś z tym zrobić.

I this one

Znajdziesz przypadki klasy A potrzebuje instancję B i B musi wystąpienie A. Jest to typowy przypadek uzależnienia kołowego i jest oczywiście złe. Z mojego doświadczenia wynika, że ​​rozwiązaniem jest albo uczynienie B częścią A, gdy są tak silnie zależni, że naprawdę powinni być jedną klasą. Częściej jednak istnieje co najmniej jedna klasa C ukrywa się tam tak, że B nie potrzebuje, ale tylko C.

Jako Oliver Gerke commented:

Szczególnie wtrysk konstruktor faktycznie uniemożliwia wprowadzanie cyklicznych zależności. Jeśli je wprowadzisz, to w istocie uczynisz dwie strony jedną, ponieważ nie możesz tak naprawdę zmienić jednego bez ryzyka złamania drugiego, co w każdym przypadku jest zapachem projektowym.

Oto mały przykład tego, co mogę zrobić.

public class A { 

    private B b; 

    @Autowired 
    public A(B b) { 
     this.b = b; 
    } 

    public void doSomeWork() { 
     // WORK 
    } 

    public void doSomeWorkWithB() { 
     b.doSomeWork(); 
    } 
} 

public class B { 

    private A a; 

    @Autowired 
    public B(A a) { 
     this.a = a; 
    } 

    public void doSomeWork() { 
     // WORK 
    } 

    public void doSomeWorkWithA() { 
     a.doSomeWork(); 
    } 

} 

Po refaktoryzacji może wyglądać tak.

public class A { 

    private C c; 

    @Autowired 
    public A(C c) { 
     this.c = c; 
    } 

    public void doSomeWork() { 
     // WORK 
    } 

    public void doSomeWorkWithC() { 
     c.doSomeWorkThatWasOnA(); 
    } 

} 

public class B { 

    private C c; 

    @Autowired 
    public B(C c) { 
     this.c = c; 
    } 

    public void doSomeWork() { 
     // WORK 
    } 

    public void doSomeWorkWithC() { 
     c.doSomeWorkThatWasOnB(); 
    } 

} 

public class C { 

    public void doSomeWorkThatWasOnB() { 
     // WORK 

    } 

    public void doSomeWorkThatWasOnA() { 
     // WORK 
    } 

} 
+1

Istnieje wiele lektur, które można zrobić na linki, które zamieściłem, szczęśliwe czytanie i kodowanie! : D –

+0

Dziękuję bardzo. Czy możesz powiedzieć, jak nazwałbym metodę z C. Czy muszę skopiować całą logikę tam? – Patan

+0

Przebudowujesz kod z A i B, więc nie ma połączenia od A do B i B do A, przenosząc logikę do C –

0

Do rozwiązania tego problemu można także użyć Dependency Depression Injection.

+0

W jaki sposób Wsparcie? –

+0

Jeśli masz zależność Injection przez konstruktor, ClassA potrzebuje ClassB, a ClassB wymaga instancji ClassA, to tworzy zależność cykliczną. Jeśli używasz opartego na seterach DI, najpierw tworzona jest klasa A lub ClassB, później instanowane obiekty są używane do ustawiania właściwości względem siebie, która przerywa cykliczną zależność. –

+0

Nie o to chodzi. Zobacz moją odpowiedź. –

2

Powołując się z części 5 CDI Specification 1.2:

Pojemnik jest wymagane do obsługi circularities na wykresie zależności fasoli w którym co najmniej jeden fasoli udział w każdej zamkniętego łańcucha zależności ma normalną zakres, jak zdefiniowano w Normalne zakresy i pseudo-zakresy. Kontener nie jest wymagany, aby wspierać kołowe łańcuchy zależności, w których każdy komponent bean uczestniczący w łańcuchu ma pseudo zakres.

ApplicationScoped to normalny zakres, więc ten cykl powinien działać.

W Twojej próbce klasa A nie może być proxy, ponieważ brakuje konstruktora zerowego argumentu. Dodając ten konstruktor (który może mieć chronioną lub widoczną paczkę), próbka zostanie wdrożona bez problemów.

+0

To poprawnie rozwiązuje problem, jeśli brakuje klasy "A", jest to konstruktor no-arg na źródle Patana. –

0

Istnieje na to rozwiązanie. Samodzielnym rozwiązaniem jest quote:

Właściwe rozwiązanie polega na wstrzyknięciu javax.enterprise.inject.Instance, gdzie T jest typem klasy, która ma zostać wstrzyknięta. Ponieważ typ jest bezpośrednio Foo, wywołanie metody get() na obiekcie wpisanym jako Instancja gwarantuje ciągłe wstawianie odpowiedniego obiektu. To podejście działa bardzo dobrze, ponieważ instancja jest pobierana dynamicznie z kontenera przez samą implementację i tylko w razie potrzeby. Dlatego odpowiedzialność za pobieranie zależności spoczywa na twoim kodzie - twój kod jest odpowiedzialny za niekończącą się pętlę.

@Named 
public class Foo implements Fooable{ 

@Inject 
private Instance<Foo> foo; 

public void executeFirst(){ 
foo.get().executeSecond(); 
} 

@Transactional 
public void executeSecond(){ 
//do something 
} 

} 
Powiązane problemy