sobota, 23 października 2010
Warsjava 2010
Miałem dziś możliwość/przyjemność uczestniczyć w konferencji Warsjava 2010. Wielkie dzięki organizatorom. Było 6 konferencji, ciekawych i ciekawszych. Na pewno poszperam w Clojure, dzięki Marcinowi Rzewuckiemu - dzięki. Pomimo małych wpadek w "egit i przyjaciele" zachęcają do co najmniej rozważenia gita przy kolejnym wyborze systemu kontroli wersji. Generalnie konferencję oceniam jako udaną. Poszerzyła moje horyzonty i zainspirowała do własnych poszukiwań i rozwoju.
niedziela, 17 października 2010
Stawiamy springa na nogi, tym razem adnotacyjne
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.
sobota, 9 października 2010
Smaczki konfiguracji xml-owej
Dziedziczenie definicji bean-ów.
W xml-u mamy możliwość zdefiniowania 'dziedziczenia' beanów. Jeśli dodamy atrybut parent do taga bean mówimy springowi : "weź wszystkie propertiesy z parenta i wstrzyknij i mnie".
Konwersja wstrzykiwanych wartości.
Czasem chcemy wstrzyknąć explicite jakąś wartość podaną w xml-u (a nie referencję do innego beana). W xml-u możemy wprowadzić tylko tekst, co zatem z konwersją ? Spring udostępnia zbiór predefiniowanych konwerterów i po typie wartości jaką chcemy ustawić próbuje konwertować tekst z xml-a. Przykład :
W pacieke org.springframework.beans.propertyeditor możemy znaleźć je wszystkie, oto podstawowa lista :
Każdy bean w fabryce springa posiada unikalny identyfikator id. Jeśli explicite go nie nadamy to spring domyślnie nada id takie jak nazwa klasy z pierwszą literą pomniejszoną. Dzięki id możemy naszego beana wstrzykiwać innym beanom oraz możemy z fabryki springa pobrać jego instancję.
Atrybut id posiada kilka wad :
Wadą konfiguracji xml-owej jest to, że potrafi bardzo się rozrosnąć. Spring wychodzi temu naprzeciw dodając w wersji 3 rozszerzenia upraszczające wiele z konfiguracji. W poprzednich postach już pojawiły się :
W xml-u mamy możliwość zdefiniowania 'dziedziczenia' beanów. Jeśli dodamy atrybut parent do taga bean mówimy springowi : "weź wszystkie propertiesy z parenta i wstrzyknij i mnie".
<bean id="commonProperty" class="" > <property name="url" value="http://slawekturowicz.blogspot.com" /> <property name="author" value="slawek" /> </bean> <bean id="exampleInteritanceBean" class="" parent="commonProperty" > <property name="author" value="maciek" /> </bean>W tym przykładzie "exampleInteritanceBean" będzie miał wstrzyknięte dwa propertisy :
- "url" o wartości : "http://slawekturowicz.blogspot.com"
- "author" o wartości : "maciek".
<bean id="commonProperty" class="" abstract="true" > <property name="url" value="http://slawekturowicz.blogspot.com" /> <property name="author" value="slawek" /> </bean>
Konwersja wstrzykiwanych wartości.
Czasem chcemy wstrzyknąć explicite jakąś wartość podaną w xml-u (a nie referencję do innego beana). W xml-u możemy wprowadzić tylko tekst, co zatem z konwersją ? Spring udostępnia zbiór predefiniowanych konwerterów i po typie wartości jaką chcemy ustawić próbuje konwertować tekst z xml-a. Przykład :
<bean id="postCodeValidator" class="pl.turo.spring.validator.PostCodeValidator" > <property name="pattern" value="[0-9][0-9]-[0-9][0-9][0-9]" /> </bean>i klasa :
puclic class PostCodeValidator { private Pattern pattern; public setPattern(Pattern pattern) { this.pattern = pattern; } }Co tu się dzieje ? - wstrzykujemy do property : "pattern" wartość (tekst) : "[0-9][0-9]-[0-9][0-9][0-9]". Spring widzi, że oczekiwanym typem jest Pattern i szuka odpowidniego Property Editora. Do klasy Pattern posiada "out-of-the-box", więc go używa i stara się dokonać konwersji. Fajne, nie ?
W pacieke org.springframework.beans.propertyeditor możemy znaleźć je wszystkie, oto podstawowa lista :
- NumberEditor
- BooleanEditor
- DateEditor
- ResourceEditor
- PropertiesEditor
- LocaleEditor
- setAsText(String) do konwersji ze stringa
- getAsText() do sformatowania wartości (ta metoda jest opcjonalna).
public class PatternEditor extends PropertyEditorSupport { public void setAsText(String text) { setValue(text != null ? Pattern.compile(text) : null); } public String getAsText() { Pattern value = (Pattern) getValue(); return (value != null ? value.pattern() : ""); } }Należy jeszcze zarejestrować nasz edytor w springu. Można to zrobić np tak:
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer"> <property name="customEditors"> <map> <entry key="java.util.regex.Pattern"> <bean class="pl.turo.spring.editor.PatternEditor" /> </entry> </map> </property> </bean>Nazewnictwo beanów.
Każdy bean w fabryce springa posiada unikalny identyfikator id. Jeśli explicite go nie nadamy to spring domyślnie nada id takie jak nazwa klasy z pierwszą literą pomniejszoną. Dzięki id możemy naszego beana wstrzykiwać innym beanom oraz możemy z fabryki springa pobrać jego instancję.
Atrybut id posiada kilka wad :
- może mieć tylko jedną wartość
- nie można w nim umieścić pewnych znaków np '/' , ';' itd.
<bean name="dataSource, dataSource/primary" .../>
Wadą konfiguracji xml-owej jest to, że potrafi bardzo się rozrosnąć. Spring wychodzi temu naprzeciw dodając w wersji 3 rozszerzenia upraszczające wiele z konfiguracji. W poprzednich postach już pojawiły się :
- namespace util:
<util:list> <value>/datasource.properties</value> <value>/user.properties</value> </util:list>
Analogicznie mamy również util:map oraz util:set do deklarowania map oraz zbiorów.
Możemy również wstrzykiwać wartości publicznych pól statyczny w klasach :
<bean class="pl.turo.spring.ExampleService"> <property name="value"> <util:constant static-field="pl.turo.spring.ExampleRepository.STATIC_VALUE"/> </property> </bean>
- namespace context:
Odnosi się do konfiguracji fabryki springa, np :
<context:property-placeholder location="/datasource.properties"/> <context:component-scan base-package="pl.turo.spring" />
- namespace aop
<aop:scoped-proxy />O możliwościach Spring AOP i samej konfiguracji będę szerzej pisał w dalszych postach.
- p-namespace :
Aby wstrzyknąć zależność nie musimy deklarować taga <property> a zamiast tego mamy atrybut w tagu bean, po 'staremu' :To tyle odnośnie xml-a, następnym razem opiszę jak konfigurować springa adnotacyjne.
</property>
<bean name="example" class="pl.turo.spring.Repository"> <property name="dataSource" ref="dataSource"/> </bean>korzystając z p-namespace :
<bean name="example" class="pl.turo.spring.Repository" p:dataSource-ref="dataSource"/>
Subskrybuj:
Posty (Atom)