Wszystkie posty spełniające kryteria zapytania PropertyPlaceholderConfigurer, posortowane według trafności. Sortuj według daty Pokaż wszystkie posty
Wszystkie posty spełniające kryteria zapytania PropertyPlaceholderConfigurer, posortowane według trafności. Sortuj według daty Pokaż wszystkie posty

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ś.

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.

piątek, 21 stycznia 2011

Transakcyjność w springu

Czołem.
Dziś zaprezentuję jak w springu skonfigurować transakcyjność. Najpierw przypomnijmy kilka pojęć.

Propagacja transakcji polega na zdefiniowaniu zachowania transakcji przy wywołaniu metody wewnątrz innej transakcyjnej metody.

Jeśli metoda A jest uruchomiona w traksakcji to co ma się wydarzyć, gdy wywoła ona metodę B, która również jest transakcyjna? Czy transakcja powinna się propagować czy nie? Czy może B powinna być uruchomiona w swojej transakcji? Jest to konfigurowalne. Spójrzmy na tabelę z opisem 3 podstawowych poziomów propagacji: