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.
Pierwsze będzie pokazywało 'klasyczny', stary, xml-owy sposób.
Wspomniany PropertyPlaceholderConfigurer (nadajmy mu alias 'PPC') jest BeanFactoryPostProcessorem, który przed utworzeniem beanów przegląda konfigurację fabryki springa i podmienia wszystkie stringi w postaci ${'klucz'} na wartość o podanym kluczu. Wartości bierze ze swojej konfiguracji: mogą to być pliki properties, bądź wartości podane 'ręcznie'.
Zatem jeśli do PPC podamy plik proterties z wpisem "user=ryszard" i gdzieś w xml (konfiguracji springa) mamy wpis ${user}, to PPC podmieni go na napis "ryszard". Zauważmy, że mówimy o wartościach w xml-u.
Dla przykładów zdefiniujmy sobie interfejs:
Co się stało:
Podsumowanie.
Podejście drugie również będzie dotykało xml-a i PPC, jednak z uproszczoną składnią. Spójrzmy na konfigurację:
Podejście trzecie korzysta z dobrodziejstw SpEL (Spring Expression Language). Spójrzmy:
Podejście czwarte jest wariancją trzeciego. Napisłem, że adnotacja @Value potrzebuje odwołać się do beana typu java.util.Properties. Nic nie stoi zatem na przeszkodzie aby utworzyć tego beana w jeden z wielu sposobów w springu, np tak:
Zauważmy, że tutaj możemy podać listę wielu plików properties. Reszta działa jak w przypadku trzecim.
(Opis jak działa fabryka springa można znaleźć m.in. w moim wcześniejszym poście.)
Następnym razem opiszę kilka smaczków dotyczących obsługi propertisów w springu.
Stąd można pobrać projekt, w którym testowałem opisywane funkcjonalności.
Pokażę dzisiaj 4 różne podejścia.
Pierwsze będzie pokazywało 'klasyczny', stary, xml-owy sposób.
Wspomniany PropertyPlaceholderConfigurer (nadajmy mu alias 'PPC') jest BeanFactoryPostProcessorem, który przed utworzeniem beanów przegląda konfigurację fabryki springa i podmienia wszystkie stringi w postaci ${'klucz'} na wartość o podanym kluczu. Wartości bierze ze swojej konfiguracji: mogą to być pliki properties, bądź wartości podane 'ręcznie'.
Zatem jeśli do PPC podamy plik proterties z wpisem "user=ryszard" i gdzieś w xml (konfiguracji springa) mamy wpis ${user}, to PPC podmieni go na napis "ryszard". Zauważmy, że mówimy o wartościach w xml-u.
Dla przykładów zdefiniujmy sobie interfejs:
public interface Service { String getMessage(); }Napiszmy sobie implementację interfejsu:
public class XmlStyleService implements Service { private String name; private String surname; public String getMessage() { return "Hello " + name + " " + surname; } public void setName(String name) { this.name = name; } public void setSurname(String surname) { this.surname = surname; } }do tego konfiguracja w xml-u:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="location" value="classpath:config.properties" /> </bean> <bean id="xmlOldStyleService" class="pl.turo.spring.impl.XmlStyleService"> <property name="name" value="${imie}" /> <property name="surname" value="${nazwisko}" /> </bean>Trzeba jeszcze utworzyć plik properties, ja go sobie nazwę 'config.properties':
imie=ryszard nazwisko=tur
Co się stało:
- Zdefniniowałem beana typu PropertyPlaceholderConfigurer-a i podałem mu plik config.properties. Zauważmy, że nie potrzeba mu nadawać id, ponieważ istotne jest tylko to, że pojawi się w konfiguracji - nigdzie wstrzykiwany nie będzie.
- Zdefniniowałem beana naszego serwisu xmlOldStyleService, gdzie pojawiają się dwie kluczowe linie konfiguracji: <property name="name" value="${imie}" /> <property name="surname" value="${nazwisko}" />
- Spring przed utworzeniem obiektów, po przeczytaniu i zwalidowaniu konfiguracji, uruchamia post processory, między innymi naszego PropertyPlaceholderConfigurer-a.
- Ów PPC przelatuje się po całej konfiguracji i szuka wyrażeń ${'klucz'}.
- Znajdzie nasze dwie linie i szuka w wartości dla podanych kluczy ('imie', 'nazwisko'). Podaliśmy je w pliku config.properties, zatem podmieni te wartości.
- Z podmienionymi wartościami utworzy beana xmlOldStyleService.
@ContextConfiguration(locations={"classpath:/pl/turo/spring/xml-old-style-context.xml"}) @RunWith(SpringJUnit4ClassRunner.class) public class OldStyleServiceTests extends TestCase { @Autowired private Service xmlOldStyle; @Test public void testXmlOldStyle() throws Exception { assertEquals("Hello ryszard tur", xmlOldStyle.getMessage()); } }Jak to mówią: "u mnie działa" ;-).
Podsumowanie.
- Tworzymy PPC, podajemy mu propertisy (tak jak ja z pliku, można i na inne sposoby).
- Tam gdzie chcemy w xml-u wstrzyknąć wartości z propertisów, piszemy ${'klucz'}, gdzie klucz to klucz naszej wartości.
Podejście drugie również będzie dotykało xml-a i PPC, jednak z uproszczoną składnią. Spójrzmy na konfigurację:
<context:property-placeholder location="classpath:config.properties" /> <bean id="xmlNewStyleService" class="pl.turo.spring.impl.XmlStyleService"> <property name="name" value="${imie}" /> <property name="surname" value="${nazwisko}" /> </bean>Namespace 'context' upraszcza i skraca zapis konfiguracji PPC. Działa identycznie jak w przypadku pierwszym. Różnica jaką udało mi się wyłapać, zresztą dość istotna, jest taka że tutaj możemy podać tylko jeden plik *.properties (chociaż jest na to sposób - pokażę w kolejny poście).
Podejście trzecie korzysta z dobrodziejstw SpEL (Spring Expression Language). Spójrzmy:
public class AnnotationStyleService implements Service { @Value("#{prop.imie}") private String name; @Value("#{prop.nazwisko}") private String surname; public String getMessage() { return "Hello " + name + " " + surname; } }oraz konfiguracja:
<util:properties id="prop" location="classpath:config.properties" /> <bean id="annotationStyleService" class="pl.turo.spring.impl.AnnotationStyleService" />Przeanalizujmy:
- Korzystając z namespace 'util' tworzymy beana typu java.util.Properties i podajemy mu plik, z którego bean zaczyta sobie dane (mapa klucz->wartość).
- Należy zwrócić uwagę na to, że tym razem wymagane jest podanie id dla tego bena (w odróżnieniu od PPC). Id posłuży nam w adnotacji @Value na określenie skąd spring ma pobrać wartość do wstrzyknięcia.
- Zatem zapis @Value("#{prop.imie}") należy rozumieć: z beana o id="prop" (który musi być typu java.util.Properties) weź wartość na kluczu 'imie'.
- Definiujemy naszego beana bez wstrzykiwania wartości w xml-u.
@ContextConfiguration(locations={"classpath:/pl/turo/spring/annotation-style-context.xml"}) @RunWith(SpringJUnit4ClassRunner.class) public class AnnotationServiceTests extends TestCase { @Autowired private Service annotationStyleService; @Test public void testValueAnnotationStyle() throws Exception { assertEquals("Hello ryszard tur", annotationStyleService.getMessage()); } }I jak poprzednio: "u mnie działa".
Podejście czwarte jest wariancją trzeciego. Napisłem, że adnotacja @Value potrzebuje odwołać się do beana typu java.util.Properties. Nic nie stoi zatem na przeszkodzie aby utworzyć tego beana w jeden z wielu sposobów w springu, np tak:
<bean id="prop" class="org.springframework.beans.factory.config.PropertiesFactoryBean"> <property name="locations"> <list> <value>classpath:config.properties</value> </list> </property>
</bean> <bean id="annotationStyleService" class="pl.turo.spring.impl.AnnotationStyleService" />Skorzystałem tutaj z dostarczonej przez springa fabryki beanów oczekiwanego przez nas typu (java.util.Properties). Jak poprzednio nadałem temu beanowi id (zgodne z tym co mamy w adnotacji @Value).
Zauważmy, że tutaj możemy podać listę wielu plików properties. Reszta działa jak w przypadku trzecim.
(Opis jak działa fabryka springa można znaleźć m.in. w moim wcześniejszym poście.)
Następnym razem opiszę kilka smaczków dotyczących obsługi propertisów w springu.
Stąd można pobrać projekt, w którym testowałem opisywane funkcjonalności.
plus kozak opcja - podmiana wartosci z plikow .properties przez nadpisywanie ich zmiennych systemowymi lub parametrami wywolania mavena.
OdpowiedzUsuńWydaje mi się, że mówimy o dwóch różnych rzeczach: ja piszę o pracy z propertisami w springu (runtime) a Ty piszesz o filtrowaniu zasobów przez mavena w czasie budowania projektu. Można zrobić nawet więcej niż nadpisywanie propertisów wartościami ze zmiennych systemowych: a czemu nie pobierać ich z jakiegoś serwera konfiguracyjnego? Można by mnożyć pomysły ;)
OdpowiedzUsuńPozdrawiam.