piątek, 17 grudnia 2010

Spring i wartości *.properties

Ostatnio stanąłem przed koniecznością skonfigurowania nowego projektu w springu. Jedną z rzeczy do zrobienia było czytanie parametrów z plików *.properties. W jednym z pierwszych postów pisałem jak posługiwać się PropertyPlaceholderConfigurer-em, dziś postaram się uzupełnić i spójnie przedstawić tą kwestię w springu.

Pokażę dzisiaj 4 różne podejścia.

poniedziałek, 22 listopada 2010

Certified Spring Professional

Certyfikat SpringSource Certified Spring Professional zdobyty! Wynik to 88%. Powiem szczerze, że wymagał sporo nauki. Egzamin składa się z 50 pytań wielokrotnego wyboru, czas to 88 minut.
Dla potomnych przygotowujących się do egzaminu polecam:
  1. http://springcert.sourceforge.net/
  2. Scott Selikoff's blog
  3. java black belt mock exam
Oraz obowiązkowo materiały ze szkolenia Core Spring oraz dokumentacja.

Java black belt mock exam w moim odczuciu odbiega sporo od zakresu egzaminu, niemniej jednak polecam gdyż można sprawdzić stan swojej wiedzy.

Płatny test na skill guru moim zdaniem (w przeciwieństwie do innych opinii) nie przygotowuje do egzaminu - także nie polecam. Są tam pytania o moduły spoza zakresu (np. SpEL), za mało pytań o konfigurację fabryki oraz cykl życia, brak też dobrych pytań o AOP.

Co dalej? Myślę, że uzupełnię wpisy na blogu o moduły których się uczyłem a nie zdążyłem tutaj zamieścić.

środa, 10 listopada 2010

Jdbc w spring

Dziś omówię dostęp do danych za pomocą JdbcTempate.
Praca z jdbc jest nużąca i nastręcza wiele problemów, stąd zapewne powstały w javie i są bardzo popularne różny ORM-y. Czemu nie piszę zatem o JPA czy hibarnate (przyjdzie i na to czas) - otóż uważam, że w pewnych sytuacjach jdbc jest idealnym wyborem w dostępie do danych. Jak to mawia mój kolega: "nie strzelajmy z armaty do muchy".
Pracując z jdbc musimy obsługiwać transakcje, zarządzać połączeniem, obsługiwać wyjątki (a cóż można zrobić jak poleci SQLException przy zamykaniu połączenia ?). Generalnie trzeba napisać mnóstwo kodu technicznego, obsługującego specyfikę jdbc, aby móc wykonać jakikolwiek zapytanie. I pewnie jest to świetne miejsce aby skorzystać z dobrodziejstw aspektów. Można, ale nie trzeba - inni (deweloperzy ze springsource) zrobili to już za nas dając JdbcTemplate.

poniedziałek, 8 listopada 2010

Spring aop - różne typy metod advice

W pierwszym poście na temat spring aop wspomniałem o typach advice-ów :
Typy advice-ów :
  • before - advice uruchomi się przed wykonaniem join point-a.
  • afterReturning - advice uruchomi się po poprawnym wykonaniu join point-a.
  • afterThrowing - advice uruchomi się po rzuceniu wyjątku z join point-a.
  • after - advice uruchomi się niezależnie od tego czy metoda wykonała się bezbłędnie czy nie.
  • around - advice uruchomi się po before, sami tutaj odpowiadamy za wywołanie join point-a i zwrócenie wartości.

Spring AOP - definiowanie poincut-ów cz. 2

Post ten nawiązuje do dwóch poprzednich w tematyce spring aop. Będę opierał się o ten sam projekt.
Definiując pointcut możemy uzyskać dostęp do kontekstu join point-a. Do tego kontekstu należą:
  • this - oznacza rzeczywisty obiekt, na którym została wywołana metoda (proxy)
  • target - oznacza obiekt zawierający join point (bean springa, którego metodę 'aspektujemy')
  • argumenty wywołania metody
  • adnotacje związane z metodą.
Są to informacje, które mogą się okazać użyteczne podczas implementacji kodu advice.

niedziela, 7 listopada 2010

Spring AOP - definiowanie poincut-ów cz. 1

Post ten nawiązuje do poprzedniego posta o spring aop.
Przypomnijmy, że spring aop pozawala na aspektowanie wyłącznie metod publicznych na beanach.
Pointcut to 'wyrażenie regularne' pozwalające springowi znaleźć interesujące nas metody. Spring zaczerpnął sposób ich definiowania z AspectJ.

Ogólny szablon wygląda tak :
execution(typ_zwracany pattern_na_ścieżkę_pakietowa_do_klasy? pattern_na_nazwę_metody(pattern_na_parametry) 
pattern_na_deklarowane_wyjątki?)
gdzie oczywiście opcjonalne wartości są zaznaczone znakiem '?'.

sobota, 6 listopada 2010

Wstęp do Spring AOP.

W dzisiejszym wpisie wystąpi trochę anglojęzycznych zwrotów - ucząc się AOP nie próbowałem niektórych pojęć tłumaczyć i zapożyczyłem je, jeśli szanowny Czytelnik ma pomysł na tłumaczenia proszę o komentarz.

Aby zrozumieć czym jest, a może ważniejsze - po co jest AOP spójrzmy na dwa problemy występujące w systemach :
  1. "Tangling" - mówi o tym, że mamy pomieszany kod biznesowy z kodem obsługującym : transakcyjność, logowanie, obsługę wyjątków itp. - jakiś kod techniczny.
  2. "Scattering" - mamy podobny kod rozsiany po całym systemie - np kod obsługi transakcyjności, otwieranie sesji hibernate itp.
AOP mówi o tym, żeby takie kawałki kodu znaleźć, wyekstrahować do osobnego bytu i zaaplikować do kodu biznesowego. Oddzielamy kod biznesowy od kodu technicznego. Mówiąc po ludzku : z kodu biznesowego wyciągamy to, co nie jest kodem logiki biznesowej, następnie umieszczamy go w innej klasie, dalej AOP 'automagicznie' sprawia, że w czasie wykonania wyjęty przez nas kod mimo wszystko zostanie uruchomiony.

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 :
<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".
<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".
Zauważmy, że można nadpisywać wartości. W powyższym przykładzie spring utworzy dwa beany "commonProperty" oraz "exampleInteritanceBean". Możemy nie chcieć tworzyć instancji parenta - w momencie, gdy służy nam do wyekstrachowania zbioru wartości, możemy dodać atrybut abstract="true" w tagu bean.
<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
Można zaimplementować swój własny PropertyEditor, wystarczy podziedziczyć po PropertyEditorSupport i zaimplementować dwie metody :
  1. setAsText(String) do konwersji ze stringa
  2. 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.
Jeśli potrzebujemy ominąć te restrykcje to możemy użyć atrybutu name zamiast id (albo wraz z id)
<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' :
</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"/>
To tyle odnośnie xml-a, następnym razem opiszę jak konfigurować springa adnotacyjne.

niedziela, 26 września 2010

Za kulisami fabryki springa cz.2

O instancjonowaniu beanów.

Spring tworząc obiekty korzysta z refleksji i oczekuje istnienia konstruktora bezparametrowego. Widoczność nie znaczenia. Co w przypadku gdy nie mamy możliwości bądź nie chcemy takiego konstruktora udostępniać ? Istnieją inne metody.

Możemy utworzyć statyczną metodę fabrykującą. Spójrzmy na klasę i jej konfigurację :
public class ExampleService implements Service {
    private Repository repo;

    public ExampleService(Repository repository) {
        super();
        this.repo = repository;
    }

    private static ExampleService newInstance(Repository repository) {
        return new ExampleService(repository);
    }
}

oraz konfiguracja :
<bean id="service" class="pl.turo.spring.service.ExampleService" factory-method="newInstance">
    <constructor-arg ref="repository" />
</bean>

<bean id="repository" class="pl.turo.spring.repository.ExampleRepository" />  
Spring tworząc beana "service" nie będzie szukał konstruktora z parametrem zgodnym typem z Repository, ale na klasie ExampleService uruchomi metodę newInstance i jej w parametrze przekaże referencję do beana "repository". Jak widać metoda ta może być prywatna. Jeśli chcielibyśmy mieć taką metodę w innej klasie to nie ma problemu :
public class ServiceFactory {
    private Repository repository;

    public ExampleService newService() {
        return new ExampleService(repository);
    }

    public void setRepository(Repository repository) {
        this.repository = repository;
    }
}
oraz konfiguracja :
<bean id="serviceFactory" class="pl.turo.spring.service.ServiceFactory">
 <property name="repository" ref="repository" />
</bean>

<bean id="service" class="pl.turo.spring.service.ExampleService"
 factory-method="newService" factory-bean="serviceFactory" />

<bean id="repository" class="pl.turo.spring.repository.ExampleRepository" />
Tym razem spring tworząc beana "service" oddeleguje utworzenie obiektu do bena-fabryki ("serviceFactory") i na nim wywoła metodę newService.
Zauważmy, że przeciwnie do pierwszego przykładu - tutaj tworzymy beana będącego fabryką, więc metoda nie jest już statyczna.

Kolejną możliwością jest zaimplementowanie interefejsu FactoryBean. Beany takie są automatycznie wykrywane przez springa (BeanFactoryPostProcessor).
Przykład :
public class RepositoryFactory implements FactoryBean<Repository> {
 private boolean singleton;
 private Repository instance;
 
 public Repository getObject() throws Exception {
  if (!singleton || instance == null) {
   instance = new ExampleRepository();
  } 
  return instance;
 }

 public Class getObjectType() {
  return Repository.class;
 }

 public boolean isSingleton() {
  return singleton;
 }
 
 public void setSingleton(boolean singleton) {
  this.singleton = singleton;
 }
}
oraz konfiguracja :
<bean id="repositoryFactory" class="pl.turo.spring.repository.RepositoryFactory">
 <property name="singleton" value="false" />
</bean>

<bean id="service" class="pl.turo.spring.service.ExampleService">
 <constructor-arg ref="repositoryFactory" />
</bean>
Widzimy konfigurację fabryki. Bean tej fabryki jest użyty w miejscu, gdzie powinien pojawić się bean 'repository' i spring domyśli się, że należy uruchomić metodę getObject().

Ponieważ fabryka to najzwyklejszy w świecie bean, to można jej wstrzykiwać wartości/beany. Jedną z częściej wykorzystywanych fabryk w springu jest fabryka sesji hibernate :
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
 <property name="dataSource" ref="dataSource" />
 <property name="packagesToScan" value="pl.turo.spring" />
 <property name="schemaUpdate" value="true" />
 <property name="hibernateProperties">
  <props>
   <prop key="hibernate.format_sql">true</prop>
   <prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>
  </props>
 </property>
 <!-- dalsza konfiguracja -->
</bean>

To nie koniec możliwości. Spring 3 umożliwia tworzenie konfiguracji w kodzie javy - wystarczy dodać adnotację @Configuration do beana. Przykład :
@Configuration
public class RepositoryConfiguration {
 @Bean
 public Repository repository() throws Exception {
  RepositoryFactory repositoryFactory = new RepositoryFactory();
  repositoryFactory.setSingleton(true);
  return repositoryFactory.getObject();
 }
}
oraz :
@Configuration
@Import({ RepositoryConfiguration.class })
public class ServiceConfiguration {
 
 @Autowired
 private Repository repository;
 
 @Bean
 public Service service () throws Exception {
  return new ExampleService(repository);
 }
}
i konieczny xml :
<context:annotation-config />
<bean class="pl.turo.spring.configuration.RepositoryConfiguration" />
<bean class="pl.turo.spring.configuration.ServiceConfiguration" />
Pokazałem jak utworzyć więcej niż jeden bean konfiguracyjny i jak z jednego wyciągnąć beany aby wstrzyknąć drugiemu (repository wstrzykujemy do service).

W temacie instancjonowania obiektów należy jeszcze wspomnieć o metodach zwrotnych cyklu życia beana. Możemy chcieć aby spring uruchomił metodę beana zaraz po jego utworzeniu i wstrzyknięciu zależności. Możemy to osiągnąć na 3 sposoby :
  • adnotacją @PostConstruct z JSR-250 (metoda musi być bezparametrowa - to jedyne wymaganie)
  • atrybutem init-method w tagu bean (metoda musi być bezparametrowa - to jedyne wymaganie)
  • implementując interfejs InitalizingBean (metoda afterPropertiesSet())

Podobnie możemy chcieć wykonać jakąś akcję przed zniszczeniem beana (dokładniej tuż przed odpięciem beana od fabryki springa)
  • adnotacją @PreDestroy
  • atrybutem destroy-method w tagu bean
  • implementując interfejs DisposableBean (metod destroy())

To na tyle w tamacie instancjonowania obiektów.

wtorek, 21 września 2010

Za kulisami fabryki springa cz.1

Cykl życia springa dzieli się na 3 etapy :

1. initialization
2. use
3. destruction

Ad 1.
W tej fazie spring :

1. parsuje wszystkie xml-e
2. ładuje definicje beanów do fabryki (każdy bean pod unikalnym id)
3. uruchamiane są BeanFactoryPostProcessor-y - mogą one zmieniać definicję beanów.

Przykładem takiego postprocessora jest PropertyPlaceholderConfigurer, którym zastępowane są wyrażenia ${nazwa_wartości} wartościami (np zaczytanymi z plików .properties lub wstrzykniętymi). Uruchamiane są zanim zostaną utworzone obiekty. Można napisać własny BeanPostProcessor.
Przykład użycia PropertyPlaceholderConfigurer-a :
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
 <property name="driverClassName" value="${driver}" />
 <property name="url" value="${url}" />
 <property name="username" value="${username}" />
 <property name="password" value="${password}" />
</bean>

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
 <property name="locations">
  <util:list>
   <value>/datasource.properties</value>
   <value>/user.properties</value>
  </util:list>
 </property>
</bean>
datasource.properties :
driver=oracle.jdbc.driver.OracleDriver
url=jdbc:oracle:thin:@localhost:1521:BAZA
oraz user.properties :
username=slawek
password=tajne

Zdefiniowałem dwa bean-y. Bean-owi 'dataSource' w fazie inizjalicacji zostaną ustawione wartości wczytane z plików .properties. Spring czytając definicje bean-ów automatycznie wykrywa beana implementującego interfejs BeanFactoryPostProcessor i go uruchomi, zatem wystarczy że pojawi się definicja PropertyPlaceholderConfigurer-a. Czy można mieć kilka definicji PropertyPlaceholderConfigurer-a ? Pewnie, że można, tylko będąc świadomym jak BeanFactoryPostProcessor-y działają musimy wiedzieć, że taka konfiguracja poprawna nie będzie :
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
 <property name="driverClassName" value="${driver}" />
 <property name="url" value="${url}" />
 <property name="username" value="${username}" />
 <property name="password" value="${password}" />
</bean>

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
 <property name="location" value="/user.properties" />
</bean>

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
 <property name="location" value="/datasource.properties" />
</bean>
a dlaczego ? Otóż pierwszy PropertyPlaceholderConfigurer nie ma wczytanych wszystkich wartości (podaliśmy mu tylko to, co jest w pliku /user.properties). Zatem widząc taką wartość "${driver}" w definicji beana "dataSource" rozpoznaje jako ciąg, który należy podmienić i szuka w swojej mapie wartości o takim kluczu. Nie znajduje jej (ponieważ nie ma takiej wartości w /user.properties, jest natomiast w dasasource.properties) i skutkuje to wyjątkiem :
Caused by: org.springframework.beans.factory.BeanDefinitionStoreException: Invalid bean definition with name 'dataSource' defined in class path resource [META-INF/spring/app-context.xml]: Could not resolve placeholder 'driver'.
Rozwiązaniem problemu jest atrybut ignoreUnresolvablePlaceholders. W kolejnym poście szerzej opisałem działanie PropertyPlaceholderConfigurer'a.

Korzystając ze springa 3 możemy użyć namepsace 'context' i nieco skrócić konfigurację. Zamiast :

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
 <property name="location" value="/datasource.properties" />
</bean>
możemy mieć :
<context:property-placeholder location="/datasource.properties"/>
te zapisy są sobie równoważne.
Spring 3 daje nam jeszcze inne możliwości. Pokazałem jak można korzystać z BeanFactoryPostProcessor-ów w xml-u, spójrzmy jak to można zrobić adnotacjami :
@Service
public class PropertyBean {
 @Value(value = "#{prop.username}")
 private String user;

 @Value(value = "#{prop.password}")
 private String password;

 public String getUser() {
  return user;
 }

 public String getPassword() {
  return password;
 }
}
zakładając, że w xml - u pojawi się bean o id 'prop' który będzie typu java.util.Properties i będzie posiadał wartość o kluczu 'username' :
<util:properties id="prop" location="/user.properties">
 <prop key="password">nowe_hasło</prop>
</util:properties>
oraz powiemy springowi że mamy adnotacyjnie skonfigurowane bean-y :
<context:component-scan base-package="pl.turo.spring" />
O adnotacjach jeszcze wspomnę.
Co ciekawe password będzie miał wartość 'tajne' - jak widać nowe_hasło nie nadpisuje nam wartości zaczytanej z pliku.
Kiedy spring uruchomi już wszystkie post processory - wtedy dopiero będzie instancjonował beany. Domyślnie każdy bean jest instancjonoawny zachłannie i jest singletonem. Oba zachowania można przekonfigurować.
Instancjonowanie można zmienić atrybutem lazy-init taga bean podając wartość true. Spring używa refleksji do tworzenia obiektów, więc dla singletonów wydaje się dobrym pomysłem instancjonowanie zachłanne.
Jeśli potrzebujemy innego zasięgu, to mamy do dyspozycji :

* prototype - spring utworzy zawsze nowy obiekt przy każdym wstrzykiwaniu tego beana. Innymi słowy jeśli w kilku miejscach będziemy wstrzykiwać beana to każdorazowo otrzymamy inną instancję
* request - chodzi o HTTP request.
* session - chodzi o HTTP session.
* można również zdefiniować swój własny scope.

Tag bean posiada strybut scope, gdzie podajemy zasięg, analogicznie jest adnotacja @Scope. Załóżmy, że przedefiniowaliśmy beana PropertyBean tak, aby miał zasięg sesji. Jeśli wstrzykiwalibyśmy go do singletonowego beana pojawi się nam problem : otóż spring w momencie instancjonowania obiektów nie wie nic o sesji http i nie będzie umiał takiego PropertyBean-a nigdzie wstrzyknąć. Istnieje tutaj możliwość skorzystania z proxy - zamiast starać się utworzyć obiekt sesyjny, utwórzmy do niego proxy. Możemy to zrobić anotacyjnie bądź xml-owo. Spójrzmy :
@Scope(proxyMode=ScopedProxyMode.TARGET_CLASS, value="session")
możemy również użyć ScopedProxyMode.INTERFACES - różnica między jednym a drugim jest istotna. Pierwszy korzysta z CGLIB tworząc proxy, drugi używa dynamic proxy z JDK.
W xml-u wygląda to tak :
<bean id="repository" class="pl.turo.spring.repository.ExampleRepository" scope="session">
 <aop:scoped-proxy />
</bean>
i użyty tu jest CGLIB. Proxy to będzie odpowiedzialne za dostęp do odpowiedniego obiektu pobranego z sesji http. Czyli nasz singletonowy bean posiada referencję do proxy, które to potrafi delegować wywołania metod do odpowiedniego beana. To naprawdę działa.
To tyle na dziś.

Hello

Witam na moim blogu. Pracuję jako programista java i postanowiłem usystematyzować i gdzieś przechować moją wiedzę o springu, dlatego powstawać będzie ów blog. Mam nadzieję, że przydadzą się komuś moje luźne notatki z nauki.

Pozdrawiam.