W poprzednich postach rozpisywałem się o tworzeniu obiektów przez Springa, o tym co po drodze możemy zrobić. Do tej pory na blogu królował xml - większość rzeczy, które pokazałem konfigurowałem właśnie w xml-u. Teraz chciałbym pokazać alternatywne podejście. Od javy 5 mamy w języku java dostępne adnotacje - spring również je wspiera.
Do tej pory pokazałem wykorzystanie adnotacji :
@Configuration - nad klasą oznacza, że dana klasa zostanie przez springa wykorzystana do utworzenia beanów w kodzie.
@Import - powiązany z @Configuration, pozwala wstrzykiwać beany utworzone w innej klasie oznaczonej @Configuration
@Bean - w kontekście @Configuration nad metodą oznacza, że obiekt zwracany będzie beanem springa
@Autowired - 'auto wstrzykiwanie', o tym szerzej napiszę
@PostConstruct - adnotacja cyklu życia beana. Metoda tak zaadnotowana zostanie uruchomiona po utworzeniu beana.
@PreDestroy - adnotacja cyklu życia beana. Metoda tak zaadnotowana zostanie uruchomiona przed usunięciem beana z fabryki springa
@Service - niżej napiszę szerzej
@Value - wstrzykiwanie wartości z PropertyPlaceholderConfigurera
@Scope - określa zasięg widoczności beana.
Zacznijmy od adnotacji związanych z definiowaniem beanów. Pierwszym krokiem jaki należy wykonać aby móc korzystać z adnotacji jest kawałek xml-a, w którym powiemy springowi, że beany mamy adnotacyjnie skonfigurowane :
Teraz możemy nad klasami znajdującymi się w podanej lokalizacji skorzystać z @Component lub adnotacji dziedziczących po niej, Przykład :
Po adnotacji @Component "dziedziczą" :
* @Service
* @Repository
* @Controller (o nim więcej napiszę przy okazji spring MVC)
Możemy zaadnotować klasę którąkolwiek z tych adnotacji i spring utworzy odpowiedniego beana. Po cóż zatem mielibyśmy korzystać np z @Service zamiast poprostu z @Component ? Po co zostały stworzone takie adnotacje ? Osobne adnotacje określają osobne stereotypy. Pozwalają na podział wielowarstwowy aplikacji. Wyobraźmy sobie, że korzystamy z AOP (o czym jeszcze będę pisał) i chcemy wszystkie wywołania wszystkich metod serwisów logować. Jeśli konsekwentnie korzystalibyśmy z odpowiednich stereotypów to sprawę mamy mocno uproszczoną - łatwo będzie nam zdefiniować odpowiedni pointcut. O AOP będę jeszcze pisał i wrócę do tego problemu.
Wiemy już jak definiować beany adnotacjami. Kolej na wstrzykiwanie zależności.
W obu przypadkach jeśli spring nie znajdzie kandydata do wstrzyknięcia to rzuci wyjątkiem i nie uruchomi się - można to przekonfigurować podając wartość :
A co jeśli spring znajdzie więcej niż 1 kandydata do wstrzyknięcia ? Również rzuci wyjątkiem i nie uruchomi się. Można natomiast wstrzyknąć kolekcję beanów :
Kolejny przypadek jest taki, że chcemy wstrzyknąć explicite jakiegoś beana (po nazwie), możemy zrobić to tak :
Definiując beany typu Bar możemy określić, że któryś ma być brany jako pierwszy do wstrzykiwania:
Idźmy dalej. @Autowired i @Resource pozwalają na adnotowanie metod :
Skoro spring wspiera konfigurację zarówno adnotacyjną, jak i xml-ową to którą wybrać ? Istnieje kilka zasad pomagających rozstrzygnąć czego użyć :
* jeśli konfigurujemy beany, których źródeł nie mamy to musimy skorzystać z xml'a
* adnotacje wydają się lepsze dla często zmieniających się elementów
* można w jednej fabryce mieszać te podejścia (pytanie czy jest to dobry pomysł ?)
* pamiętajmy, że xml-a można łatwo podmienić przy testach
Osobiście uważam, że są pewne rzeczy, które lepiej konfigurować w xml, inne adnotacyjnie. Pracując w projektach z wykorzystaniem springa mam takie spostrzeżenie, że xml wymusza rozumienie tego co się robi, a adnotacje działają 'automagicznie'. Widziałem programistów, którzy pracując kilka miesięcy i korzystając z adnotacji nie wiedzą dlaczego to działa ani jak to działa. Widzą tylko 2 rzeczy :
1. Nad klasą napisz @Service,
2. Jak chcesz skorzystać z innej klasy to dodaj pole i napisz @Autowired.
Jeśli tylko tyle programista wie o springu, to niestety moim zdaniem jest to niebezpieczna sytuacja. Myślę, że dotyczy to nie tylko znajomości springa, generalnie jeśli programista nie wie dlaczego coś działa i dlaczego działa właśnie tak a nie inaczej mamy poważny problem. Co jeśli trzeba będzie zmienić domyślne zachowanie ?
Reasumując myślę, że adnotacje są świetne w kilku przypadkach (dla mnie to np : kontrolery MVC, aspekty, transakcje), lecz preferuję xml-a. Jakie jest Wasze zdanie ?
To wszystko co chciałem przekazać w temacie wstrzykiwania beanów adnotacyjnie. Następnym razem napiszę kilka słów o AOP.
Do tej pory pokazałem wykorzystanie adnotacji :
@Configuration - nad klasą oznacza, że dana klasa zostanie przez springa wykorzystana do utworzenia beanów w kodzie.
@Import - powiązany z @Configuration, pozwala wstrzykiwać beany utworzone w innej klasie oznaczonej @Configuration
@Bean - w kontekście @Configuration nad metodą oznacza, że obiekt zwracany będzie beanem springa
@Autowired - 'auto wstrzykiwanie', o tym szerzej napiszę
@PostConstruct - adnotacja cyklu życia beana. Metoda tak zaadnotowana zostanie uruchomiona po utworzeniu beana.
@PreDestroy - adnotacja cyklu życia beana. Metoda tak zaadnotowana zostanie uruchomiona przed usunięciem beana z fabryki springa
@Service - niżej napiszę szerzej
@Value - wstrzykiwanie wartości z PropertyPlaceholderConfigurera
@Scope - określa zasięg widoczności beana.
Zacznijmy od adnotacji związanych z definiowaniem beanów. Pierwszym krokiem jaki należy wykonać aby móc korzystać z adnotacji jest kawałek xml-a, w którym powiemy springowi, że beany mamy adnotacyjnie skonfigurowane :
<context:component-scan base-package="pl.turo.spring" />Czytaj więcej o tej konfiguracji.
Teraz możemy nad klasami znajdującymi się w podanej lokalizacji skorzystać z @Component lub adnotacji dziedziczących po niej, Przykład :
@Component public class Foo { }Spring widząc taką adnotację utworzy beana i nada mu id 'foo'. Taki bean może być wstrzykiwany adnotacyjnie czy też xml-owo do innych beanów.
Po adnotacji @Component "dziedziczą" :
* @Service
* @Repository
* @Controller (o nim więcej napiszę przy okazji spring MVC)
Możemy zaadnotować klasę którąkolwiek z tych adnotacji i spring utworzy odpowiedniego beana. Po cóż zatem mielibyśmy korzystać np z @Service zamiast poprostu z @Component ? Po co zostały stworzone takie adnotacje ? Osobne adnotacje określają osobne stereotypy. Pozwalają na podział wielowarstwowy aplikacji. Wyobraźmy sobie, że korzystamy z AOP (o czym jeszcze będę pisał) i chcemy wszystkie wywołania wszystkich metod serwisów logować. Jeśli konsekwentnie korzystalibyśmy z odpowiednich stereotypów to sprawę mamy mocno uproszczoną - łatwo będzie nam zdefiniować odpowiedni pointcut. O AOP będę jeszcze pisał i wrócę do tego problemu.
Wiemy już jak definiować beany adnotacjami. Kolej na wstrzykiwanie zależności.
@Service public class Bar { } @Service public class Foo { @Autowired private Bar bar; }Spring wstrzyknie referencję odpowiedniego beana. Innymi słowy pobierając z fabryki springa beana 'foo' mamy pewność że pole bar będzie wstrzyknięte. @Autowired jest adnotacją springową, zamiast niej możemy użyć @Resource (z JSR-250). Zatem możemy mieć tak :
@Service public class Foo { @Resource private Bar bar; }Jaka zatem jest różnica ? @Autowired stara się wstrzyknąć beana na podstawie typu (spring szukając kandydata do wstrzyknięcia szuka po typie), natomiast @Resource szuka po nazwie zmiennej.
W obu przypadkach jeśli spring nie znajdzie kandydata do wstrzyknięcia to rzuci wyjątkiem i nie uruchomi się - można to przekonfigurować podając wartość :
@Service public class Foo { @Autowired (required=false) private Bar bar = new DefaultBarImplementation(); }Jeśli spring nie znajdzie pasującego do wstrzyknięcia beana to nie zrobi nic. Jest to też prosty sposób na na podanie domyślnych implemetacji.
A co jeśli spring znajdzie więcej niż 1 kandydata do wstrzyknięcia ? Również rzuci wyjątkiem i nie uruchomi się. Można natomiast wstrzyknąć kolekcję beanów :
@Service public class Foo { @Autowired private List<Bar> bars; }Przy takiej konfiguracji spring utworzy i wstrzyknie listę, następnie doda do niej wszystkie pasujące typem beany.
Kolejny przypadek jest taki, że chcemy wstrzyknąć explicite jakiegoś beana (po nazwie), możemy zrobić to tak :
@Service public class Foo { @Autowired @Qualifier("barBean") private Bar bar; } @Service("barBean") public class Bar {}Nad klasą Bar podajemy id = "barBean". oraz wstrzykując referencję do niego w klasie Foo podajemy również to id korzystając z adnotacji @Qualifier.
Definiując beany typu Bar możemy określić, że któryś ma być brany jako pierwszy do wstrzykiwania:
@Service public class Bar {} @Service @Primary public class SubBar extends Bar {} @Service public class Foo { @Autowired private Bar bar; }Teraz spring wstrzyknie beana 'subBar' do beana 'foo', nie rzuci wyjątkiem mając więcej niż jeden bean jako kandydata do wstrzyknięcia, wybierze tego z adnotacją @Primary.
Idźmy dalej. @Autowired i @Resource pozwalają na adnotowanie metod :
@Service public class Foo { private Bar bar; @Autowired public void inject (Bar bar) { this.bar = bar; } }W tym przypadku spring będzie wstrzykiwał beany korzystając z metody inject (uruchomi ją w fazie inicjalizacji beanów). Będzie wstrzykiwał wszystkie parametry. @Autowired można również użyć nad konstruktorem (@Resource już nie).
Skoro spring wspiera konfigurację zarówno adnotacyjną, jak i xml-ową to którą wybrać ? Istnieje kilka zasad pomagających rozstrzygnąć czego użyć :
* jeśli konfigurujemy beany, których źródeł nie mamy to musimy skorzystać z xml'a
* adnotacje wydają się lepsze dla często zmieniających się elementów
* można w jednej fabryce mieszać te podejścia (pytanie czy jest to dobry pomysł ?)
* pamiętajmy, że xml-a można łatwo podmienić przy testach
Osobiście uważam, że są pewne rzeczy, które lepiej konfigurować w xml, inne adnotacyjnie. Pracując w projektach z wykorzystaniem springa mam takie spostrzeżenie, że xml wymusza rozumienie tego co się robi, a adnotacje działają 'automagicznie'. Widziałem programistów, którzy pracując kilka miesięcy i korzystając z adnotacji nie wiedzą dlaczego to działa ani jak to działa. Widzą tylko 2 rzeczy :
1. Nad klasą napisz @Service,
2. Jak chcesz skorzystać z innej klasy to dodaj pole i napisz @Autowired.
Jeśli tylko tyle programista wie o springu, to niestety moim zdaniem jest to niebezpieczna sytuacja. Myślę, że dotyczy to nie tylko znajomości springa, generalnie jeśli programista nie wie dlaczego coś działa i dlaczego działa właśnie tak a nie inaczej mamy poważny problem. Co jeśli trzeba będzie zmienić domyślne zachowanie ?
Reasumując myślę, że adnotacje są świetne w kilku przypadkach (dla mnie to np : kontrolery MVC, aspekty, transakcje), lecz preferuję xml-a. Jakie jest Wasze zdanie ?
To wszystko co chciałem przekazać w temacie wstrzykiwania beanów adnotacyjnie. Następnym razem napiszę kilka słów o AOP.
Wiele zrozumiałem dzieki temu postowi:).
OdpowiedzUsuńTylko brakuje mi czegoś o @Repository który chyba również dziedziczy po @Component.
ta strona mi sie wiesza na czterordzeniowym procesorze i 8 gb pamieci, musialem skopiowac jej treść do notatnika bo w przegladarce nie dalo sie jej przegladac
OdpowiedzUsuńŚwietne wyjaśnienie ;) Jasne i klarowne ;)
OdpowiedzUsuń