Zrozumienie konieczności bezpieczeństwa typu w CDI

Najpierw powinienem wyjaśnić, że ten post nie ma na celu krytykować CDI, ale odkryć myślenie i założenia stojące za projektem CDI i to będzie miało oczywisty wpływ na projektowanie każdej aplikacji internetowej, która używa CDI.

Jedną z najbardziej wyróżniających cech CDI (Java EE 6) jest bezpieczeństwo typu . Jboss Seam nie był bezpieczny w typie. Używa nazwy, aby zakwalifikować dowolną instancję do wstrzyknięcia. Jak poniżej:
   @Name("myBean")
   public class MyBean implements Bean {
     ...
   }

   @Name("yourBean")
   public class YourBean implements Bean {
     ...
   }

Podczas wstrzykiwania MyBean można to zrobić:

   @In
   private Bean myBean; //myBean is injected

   @In
   private Bean yourBean; //yourBean is injected  

I wcześniejsze wersje sprężyny (przed 3.0), ten rodzaj wtrysku występował jak poniżej:

Po prostu zdefiniuj fasolę w pliku konfiguracyjnym bean:

   <bean id="myBean" class="com.example.common.MyBean">
       ...
   </bean>

   <bean id="yourBean" class="com.example.common.YourBean">
       ...
   </bean>

I użyj named qualifier, decydując, który z nich użyć:

   @Autowired
   @Qualifier("myBean")
   private Bean bean;

   @Autowired
   @Qualifier("yourBean")
   private Bean bean; 

Ale teraz w CDI, najpierw musisz zdefiniować niestandardową adnotację Qualifier dla dowolnego typu obiektu. Następnie użyj tej adnotacji, aby zakwalifikować ten obiekt. Pod koniec dnia, kiedy patrzysz na swój kod źródłowy, widzisz, że zmarnowałeś znaczna ilość czasu na napisanie wielu niestandardowych adnotacji do iniekcji zależności. Społeczność Java zmierza w kierunku adnotacji, pozostawiając za sobą konfiguracje oparte na XML (verbose XML). Czy jest coś, co przekonałoby kogokolwiek do myślenia o tym (Bezpieczeństwo typu z niestandardowymi adnotacjami) nie jako adnotacje werbalne, ale jako doskonała i wyróżniająca się cecha CDI?

Edit:

punkty, popychane do wyróżnione

  1. Jeśli używam niestandardowego kwalifikatora dla bezpieczeństwa typu na usługę lub dao( na interfejs), to dla aplikacji o dużych rozmiarach, takich jak 1000 lub więcej klas usług lub dao z wieloma implementacjami, będzie to bałagan. Czy w przypadku dużych zastosowań możliwe jest stosowanie bezpiecznego wtrysku typu?
  2. Jeśli odpowiedź na powyższe pytanie brzmi "nie", to po co używać bezpieczeństwa typu?
  3. nawet jeśli jest możliwe zapisanie adnotacji dla typu bezpieczeństwo, w dużych aplikacjach, czy naprawdę warto unikać konfiguracji verbose XML ?
  4. kiedy właściwie potrzebuję bezpieczeństwa typu zamiast kwalifikatora nazwy fasoli?

krótka dyskusja na temat powyższych punktów

  1. nie ma zbyt wielu przypadków, kiedy rzeczywiście potrzebujesz bezpiecznego wtrysku typu, szczególnie gdy masz jedną implementację interfejsu, powinieneś użyć @Name jako kwalifikatora. Więc tak, w dużych rozmiarach zastosowanie możliwe jest stosowanie bezpieczeństwa typu, gdy jest to rzeczywiście potrzebne.
  2. oczywiście bezpieczeństwo typu jest jedną z wyróżniających cech CDI, a w zaakceptowanej odpowiedzi znajduje się niewyczerpująca lista powodów, dla których możesz wybrać bezpieczeństwo typu.
  3. ponieważ jesteś inteligentnym programistą i wiesz dokładnie, kiedy używać zabezpieczeń typu, więc zdecydowanie warto się wysilić, gdy jest to naprawdę potrzebne.
  4. większość części zaakceptowanej odpowiedzi naprawdę mówi, Kiedy potrzebujemy wpisać Bezpieczeństwo i ten artykuł jest również bardzo pomocny w zrozumieniu.

Dzięki i szczęśliwego kodowania!

Author: Sazzadur Rahaman, 2013-03-05

3 answers

Czy CDI jest gadatliwy? Czy kwalifikacje są potrzebne?

  1. Po pierwsze, nie potrzebujesz kwalifikatorów, gdy masz tylko jedną implementację interfejsu.
  2. Jeśli masz wiele implementacji interfejsu, zadaj sobie pytanie - czy musisz je rozróżniać po wdrożeniu?

    • jeśli odpowiedź brzmi nie, rozważ użycie alternatyw.
    • Jeśli odpowiedź brzmi tak, to nadal nie potrzebujesz kwalifikatorów. Oto dlaczego:

      Aby użyć Twój własny przykład:

      public class MyBean implements Bean {
          ...
      }
      
      public class YourBean implements Bean {
          ...
      }
      

      Wtedy po prostu robisz:

      @Inject MyBean bean;
      

      Lub

      @Inject YourBean bean;
      

      Jeśli nie chcesz, aby Twoje zmienne instancji były konkretnego typu i wolałbyś zobaczyć interfejs, niż zrobić to:

      private Bean bean;
      
      @Inject
      public void setBean(MyBean bean) {
          this.bean = bean;
      }
      

      Lub

      private Bean bean;
      
      @Inject
      public void setBean(YourBean bean) {
          this.bean = bean;
      }
      

      We wszystkich powyższych przypadkach jest całkowicie wolny od kwalifikatorów, absolutnie bezpieczny dla typu i zdecydowanie nie jest gadatliwy.

  3. Następnie, aby rozwinąć ostatni punkt - czy deweloper musi wybrać odpowiednie wdrożenie, czy wybór może być dokonany problematycznie?

    • jeśli deweloper wybierze, to zrób tak, jak opisano powyżej w 2.
    • Jeśli wybór może być problematyczny, to użyj producenta:

      @Produces
      public Bean obtainTheAppropriateBean(InjectionPoint ip) {
          if (meetsConditionA(ip)) {
              return getBeanImplA();
          } else if (meetsConditionB(ip)) {
              return getBeanImplB();
          } else if (...) {
              ...
          } else {
              return getDefaultBeanImpl();
          }
      }
      

      Wciąż wolny od kwalifikatorów, bezpieczny dla typu i może nadal nie wyrażony (wybór handlu dla automatyzacji).

    Zobacz ten artykuł dla doskonałego rozszerzenia tego punktu i pomysłów na to, jak korzystać z InjectionPoint API.

Czy kwalifikatory są w ogóle potrzebne?

Widzę, że to pytanie powstaje po powyższych przykładach. Odpowiedź brzmi tak, a oto niewyczerpująca lista powodów, dla których możesz wybrać ich użycie: {]}

  • w jednym z powyższych przykładów wspomniałem o wstrzykiwaniu konkretnych implementacji interfejsu, aby uniknąć używania kwalifikatorów. Jest to całkowicie w porządku, gdy kod jest wewnętrzny, a wewnętrzni programiści będą wiedzieli, który jest który. Ale co jeśli kod jest biblioteką lub frameworkiem i nie chcesz ujawniać żadnej konkretnej implementacji w publicznym API? Zdefiniuj niektóre kwalifikatory i dobrze je udokumentuj. Czym to się różni od verbose XML? Nawet jeśli Ty, jako pisarz biblioteki, wykonujesz tyle samo gadatliwej pracy, Twoi użytkownicy nie będą musieli. Zamiast tego po prostu napiszą jedno słowo powyżej punktu wtrysku i będą szczęśliwi, że nie zmusiłeś ich do napisania żadnego XML-ja osobiście byłbym bardzo bardzo szczęśliwy. :)
  • w powyższym przykładzie producenta możesz być w stanie pokryć większość przypadków, ale nie wszystkie za pomocą logiki w metodzie producenta. A może po prostu chcesz mieć możliwość nadpisania tej logiki w dowolnym punkcie wtrysku. Następnie zachowaj producenta, zrób kwalifikator i opisz jakąś konkretną implementację. Następnie użyj kwalifikatora, jeśli nie chcesz, aby logika producenta działała.
  • Wyobraź sobie sytuację, w której masz wiele interfejsów i wielu implementacji. Poszczególne implementacje mogą mieć wspólne cechy wyróżniające i te cechy są wspólne dla wszystkich interfejsów. Jako przykład, weźmy Framework Java Colections, w szczególności interfejsy List, Set i Map. Każda z nich ma wiele implementacji, ale istnieją wspólne cechy wszystkich lub niektórych interfejsów. Na przykład połączone węzły ( szybka iteracja) - think LinkedList, LinkedHashSet, LinkedHashMap; sortowane (porządkowanie) - think TreeSet, TreeMap; hash-table based (szybkie wstawianie / removal / contains) - think HashSet, LinkedHashSet, HashMap, LinkedHashMap; concurent; random access; etc. Teraz możesz zdefiniować @Linked, @Sorted, i @HashPrzypisy Następnie wstrzyknąć:

    @Inject @Linked @Hash private Map map;
    @Inject @Sorted private Map map;
    @Inject @Hash private Set set;
    @Inject @Hash private Set set;
    
    Czy warto to robić dla frameworka zbiorów? Nie zrobiłbym tego, ale mam przypadek podobny do tego, co opisuję tutaj w moim obecnym projekcie w pracy (przepraszam, nie mogę dyskutować).
  • I wreszcie, można użyć kwalifikatorów, aby przekazać parametry producentom w połączeniu z @Nonbinding. Kontynuując powyższe ramy zbiorów, zdefiniuj:

    @Qualifier
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Hash {
        @Nonbinding int capacity() default 16;
        @Nonbinding float loadFactor() default 0.75f;
    }
    

    W ten sposób można przekazać żądaną pojemność i współczynnik obciążenia producentowi, zwracając cokolwiek opartego na tabeli hash w ten sposób:

    @Inject @Hash(capacity = 256, loadFactor = 0.85f) private Set set;
    @Inject @Hash private Set set;
    @Inject @Hash(capacity = 8, loadFactor = 0.65f) private Map map;
    
Mam nadzieję, że to odpowie na twoje pytanie. To z pewnością część tego, dlaczego kocham CDI.
 16
Author: rdcrng,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-09 16:35:32

Kiedy należy używać kwalifikacji niestandardowych?

Niestandardowe kwalifikatory CDI muszą być używane tylko wtedy, gdy: (a) Twój kod (bez adnotacji) celowo wprowadził niejednoznaczność w używanym typie; (b) chcesz wprowadzić bardziej konkretny typ obiektu niż wskazany przez kod; (c) chcesz mieć bardziej elastyczny wybór typu i konfigurowalność niż zapewniony przez predefiniowany kwalifikator @Named.

Kwalifikatory rozróżniają, który typ ma zostać utworzony / wstrzyknięty przez CDI.
(a) & (b) występuje, gdy używasz typów wysokiego poziomu w kodzie, aby dać potężny ogólny algorytm, który można ponownie wykorzystać i elastycznie dostosować. Np. często jest możliwe i zachęcane zakodowanie całego algorytmu przeciwko interfejsowi, a nie określonej klasie - np. algorytmy przeciwko liście, mapie lub kolejce. Ale często algorytmy te działają dobrze tylko wtedy, gdy zobowiązujemy się do konkretnych implementacji interfejsu, takich jak SortedList, TreeMap lub PriorityQueue. Jeśli CDI ma działać jako fabryka obiektów, niosąc out object lifecycle management, inicjalizacji właściwych obiektów do wtrysku i zarządzania zależnościami, a następnie musi znać pełne szczegóły.

Niestandardowe kwalifikatory są inteligentne i konfigurowalne w sposób, w jaki dezambiguują typ, który ma być utworzony / wstrzyknięty przez CDI. @Named to instrument o wiele blunter. Dla (c), kiedy wspominam "wybór typu", mam na myśli, że programista może połączyć wiele kwalifikatorów razem, aby" logicznie wybrać " najbardziej odpowiedni typ, bez nazywania dokładnej klasy. @Named skutecznie wymaga nominacji dokładnej klasy - wiele kwalifikatorów pozwoli CDI to wypracować dla Ciebie. Konfigurowalność odnosi się do możliwości zmiany zachowania CDI w czasie wdrażania poprzez modyfikację beans.xml (nie trzeba modyfikować kodu/adnotacji). tzn. może logicznie dostroić to, co robi CDI i może to zrobić aż do czasu wdrożenia - nie dotykając kodu ani adnotacji.

Kiedy należy zadeklarować Kwalifikacje niestandardowe?

CDI custom qualifiers nie trzeba tworzyć dla każdego indywidualnego typu, który można wstrzyknąć. To jest najważniejsze.

Można użyć wielu kwalifikatorów zarówno w punkcie wtrysku, jak i w deklaracjach typu. CDI dopasowuje Typ bean plus Zestaw kwalifikatorów przy określaniu, jaki typ wstrzyknąć. Aby zmniejszyć liczbę kwalifikatorów do zadeklarowania, dobrym pomysłem jest uwzględnienie typów w wielu opisujących je atrybutach/problemach. Wtedy zamiast jednego kwalifikatora na typ, może mieć jeden kwalifikator na typ "atrybut" - nawet jeśli taki atrybut jest wielowartościowy, np. Hash / Tree/Enum (patrz następny akapit).

Po Drugie, kwalifikatory powinny inteligentnie wykorzystywać parametry, aby jeszcze bardziej zmniejszyć liczbę wymaganych deklaracji. Zamiast tworzyć 10 kwalifikatorów możesz mieć jeden kwalifikator, który ma parametr, który jest ciągiem znaków lub, najlepiej, enum, które może przyjmować 10 wartości.

Połączenie tych dwóch pomysłów (qualifiers=type attribute PLUS use qualifier parameters) w Przykład dla biblioteki zbiorów: typy mogą być opisane atrybutami Sortowanymi / Nie-Sortowanymi? Hash/Tree / Array? Linked / Unlinked? Jeśli podam zmienną

@Inject @Sorted @Organisation("hash") @Navigation("linked") Map myMap;

Następnie mam silny wybór typu, ponieważ każdy z tych aspektów jest gwarantowany, mam posortowaną linked hash map, nie wiedząc, że wymaga to bardzo konkretnej klasy w danym pakiecie.

punkty nie zostały jeszcze uwzględnione:

Jeśli używam jakiejś adnotacji z parametryzowaną nazwą fasoli, to jak to jest w rzeczywistości bezpieczne?

Silne typowanie jest gwarantowane, ponieważ musi być pełne dopasowanie z:

  • typ fasoli
  • zestaw kwalifikatorów
  • zbiór wartości parametrów

Żaden wstrzykiwany Typ nie może naruszać żadnej z tych określonych rzeczy. Zamiast myśleć o typesafety jako dopasowanie pojedynczego ciągu znaków (nazwa_pakietu.classname) pomyśl o tym jako dopasowanie wielu łańcuchów / wartości całkowicie kontrolowanych przez programistę - te same łańcuchy / wartości są używane zarówno po stronie deklaracji , jak i wtrysku, dając bezpieczne dopasowanie. Ponadto, zamiast myśleć, że musisz dopasować do najniższego poziomu konkretnych klas-pamiętaj, że możesz dopasować do klas wyższego poziomu w hierarchii dziedziczenia i inteligentne użycie kwalifikatorów @ Default na swoich typach (pamiętaj Java EE 6 "smart defaults over configuration"?) zabierze cię do preferowanego betonu typy.

Jeśli używam niestandardowego kwalifikatora dla bezpieczeństwa typu na usługę lub dao( na interfejs), to dla aplikacji o dużych rozmiarach, takich jak 1000 lub więcej klas usług lub dao z wieloma implementacjami, będzie to bałagan. Czy w przypadku dużych zastosowań możliwe jest stosowanie bezpiecznego wtrysku typu?

  • 1000 albo więcej zajęć serwisowych lub dao? Naprawdę??! To często oznaczałoby duży problem projektowy od razu. Chyba, że to jest jeden super-mega-sized-Guiness-records-aplikacja próbna dla złożonego biznesu, takiego jak Departament podatkowy lub NASA. Nawet wtedy normalne byłoby rozkładanie się na mniejsze moduły Java EE / usługi SOA ze znacznie mniejszą liczbą klas. Jeśli to jest to, co masz, możesz chcieć przyjrzeć się uproszczeniu projektu - możesz mieć znacznie większe problemy niż definicje kwalifikatorów cut-and-past...

  • Kwalifikatory są potrzebne tylko wtedy, gdy istnieje niejednoznaczność typu - tzn. wiele klasy przodków/Potomków w tej samej hierarchii typów. Często zdarza się, że stosunkowo niewiele klas usług lub DAO jest niejednoznacznych.

  • Jak opisano powyżej, nie używaj wzorca klasy one-qualifier-per-implementation-class: zamiast tego refactor używa qualifier-per-type-aspect, a także używa opisowych parametrów-per-qualifier implmentation patterns

  • Możliwe jest stosowanie bezpiecznego wtrysku typu przy dużych zastosowaniach.

Jeśli odpowiedź na powyższe pytanie brzmi "Nie", więc jaki jest sens korzystania z bezpieczeństwa typu?

  • odpowiedź brzmi " tak "
  • [[43]} zaproponowany scenariusz (1000 + klas usług/DAO) jest bardzo rzadki

Nawet jeśli jest to możliwe, aby pisać adnotacje dla bezpieczeństwa typu, w dużych aplikacjach, to naprawdę warto wysiłku, aby po prostu uniknąć verbose XML konfiguracji?

Celem CDI nie jest tylko " unikanie verbose xml konfiguracja" CDI ma następujące cele:

    [43]} wsparcie dla zakresów (w tym nowy zakres konwersacji internetowych) z zarządzaniem cyklem życia obiektów Pozwala to na wybór zależności zarówno w czasie programowania, jak i wdrażania, bez konieczności konfigurowania konfiguracji.]}
  • wsparcie dla modułowości Java EE i Architektury komponentów Java EE-modułowa struktura aplikacji Java EE jest brana pod uwagę przy rozwiązywaniu zależności pomiędzy komponentami Java EE
  • Dzięki temu możliwe jest korzystanie z dowolnego obiektu kontekstowego bezpośrednio na stronie JSF lub JSP.]}
  • możliwość dekorowania przedmiotów
  • Możliwość łączenia przechwytywaczy z obiektami za pomocą wiązań przechwytujących typesafe]}
  • model powiadamiania o zdarzeniach
  • kontekst rozmowy internetowej oprócz trzech standardowych kontekstów internetowych zdefiniowanych przez serwlety Javy Specyfikacja
  • SPI pozwalający przenośnym rozszerzeniom na czystą integrację z kontenerem

Jest to o wiele inne i bardziej użyteczne niż podstawowy plik konfiguracyjny XML zamieniony w adnotacje - nawet wcześniejsze podstawowe adnotacje Java EE 5 resource injection były inne i bardziej użyteczne niż konfiguracja XML zamieniona w adnotacje.

 4
Author: Glen Best,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-12 07:55:08

Nie jest wymagane tworzenie niestandardowego @Qualifier za każdym razem. Wystarczy utworzyć tylko jeden z jakimś parametrem. CDI będzie traktować @CustomQualifier("myBean") i @CustomQualifier("yourBean") jako różne kwalifikatory.

Ponadto taki kwalifikator już w pakiecie CDI - @ Named

Ale czasami kwalifikatory statyczne są bardzo przydatne ,na przykład (patrz @ Alternative):

@Alternative
@Qualifier
@Documented
@Retention(value=RUNTIME)
public @interface Mock

I możesz oznaczyć niektóre fasolki jako @Mock Tylko do testowania (nie zapomnij włączyć sekcji alternative w beans.xml W teście classpath.

 2
Author: Michail Nikolaev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-05 21:29:17