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.

Brak komentarzy:

Prześlij komentarz