Property Editory ve Spring Frameworku

22. March 2007

Dnešní článek bych chtěl věnovat property editorům a zvláště jejich používání ve Spring Frameworku.

K čemu slouží property editory

Představte si následující případ – máte doménový objekt cz.vavru.sprin­g.sample.busi­ness.vo.Compa­ny, který je deninován:

package cz.vavru.sample.spring.business.vo;

public class Company {
        private int id;
        private Country country;
        private java.util.Currency currency;

        public Country getCountry() {
                return country;
        }

        public void setCountry(Country country) {
                this.country = country;
        }

        public java.util.Currency getCurrency() {
                return currency;
        }

        public void setCurrency(java.util.Currency currency) {
                this.currency = currency;
        }

        public int getId() {
                return id;
        }

        public void setId(int id) {
                this.id = id;
        }

}

Vidíme, že se jedná o klasickou JavaBeanu. Referencování třída cz.vavru.sample­.spring.busines­s.vo.Country vypadá následovně:

package cz.vavru.sample.spring.business.vo;

public class Country {
        private int id;
        private String name;

        public int getId() {
                return id;
        }

        public void setId(int id) {
                this.id = id;
        }

        public String getName() {
                return name;
        }

        public void setName(String name) {
                this.name = name;
        }
}

Máte tedy nějakou společnost (Company), která je z nějaké země (Country) a v našem systému má nastavenou nějakou měnu (java.util.Cu­rrency). Záměrně jsem zvolil tyto dvě vlastnosti, protože si na nich nejlépe ukážeme použítí property editorů.A otázka nyní zní – jakým způsobem vytvořit administraci objektu cz.vavru.sample­.spring.busines­s.vo.Company například na webové stránce.

Property editory slouží k převedení objektu na String a naopak umožňují převést String na objekt požadovaného typu. Je dobré si uvědomit, že klient webovému serveru neposílá nic jiného než parametry typu String.

Interface java.beans.Pro­pertyEditor

Všechny property editory musí implementovat interface java.beans.Pro­pertyEditor. Podívejme se, co říká jeho javadoc:

A PropertyEditor class provides support for GUIs that want to allow users to edit a property value of a given type.

Property Editor supports a variety of different kinds of ways of displaying and updating property values. Most PropertyEditors will only need to support a subset of the different options available in this API.

Simple PropertyEditors may only support the getAsText and setAsText methods and need not support (say) paintValue or getCustomEditor. More complex types may be unable to support getAsText and setAsText but will instead support paintValue and getCustomEditor.

Every propertyEditor must support one or more of the three simple display styles. Thus it can either (1) support isPaintable or (2) both return a non-null String[] from getTags() and return a non-null value from getAsText or (3) simply return a non-null String from getAsText().

Every property editor must support a call on setValue when the argument object is of the type for which this is the corresponding propertyEditor. In addition, each property editor must either support a custom editor, or support setAsText. Each PropertyEditor should have a null constructor.

Z textu vyplívá, že property editor by v našem případě (použití na webové stránce) musí implementovat metody:

public void setAsText(String text)
slouží pro vytvoření objektu na základě Stringu.
public String getAsText()
naopak vrací textovou reprezentaci objektu, pro která máme napsán PropertyEditor

Naše property editory budou ve skutečnosti dědit z třídy java.beans.Pro­pertyEditorSup­port, která je základní implementací interfacu java.beans.Pro­pertyEditor.

Vytváříme vlastní property editor

Pojďme si vytvořit Property Editory pro naše dva objekty.

Property Editor pro třídu java.util.Curren­cy:

package cz.vavru.sample.spring.propertyeditor;

import java.beans.PropertyEditorSupport;

public class CurrencyPropertyEditor extends PropertyEditorSupport {

        public void setAsText(String text) throws IllegalArgumentException {
                if (str != null && str.length() > 0) {
                        java.util.Currency currency = java.util.Currency.getInstance(text);
                        super.setValue(currency);
                } else {
                        super.setValue(null);
                }
        }

        public String getAsText() {
                java.util.Currency currency = (java.util.Currency) super.getValue();
                return (currency != null ? currency.getCurrencyCode() : "");
        }
}

A property editor pro třídu cz.vavru.sample­.spring.busines­s.vo.Country včetně interfacu cz.vavru.sample­.spring.dao.i­face.CountryDA­O:

package cz.vavru.sample.spring.propertyeditor;

import java.beans.PropertyEditorSupport;

import cz.vavru.sample.spring.dao.iface.CountryDAO;
import cz.vavru.sample.spring.business.vo.Country;

public class CountryPropertyEditor extends PropertyEditorSupport {
        private CountryDAO countryDAO;

        public void setCountryDAO(CountryDAO countryDAO) {
                this.countryDAO = countryDAO;
        }

        public void setAsText(String text) throws IllegalArgumentException {
                if (str != null && str.length() > 0) {
                        try {
                                Country country = this.countryDAO.get(new Integer(text));
                                super.setValue(country);
                        } catch (NumberFormatException ex) {
                                throw new IllegalArgumentException();
                        }
                } else {
                        super.setValue(null);
                }
        }

        public String getAsText() {
                Country country = (Country) super.getValue();
                return (country != null ? country.getId().toString() : "");
        }
}

// iface cz.vavru.sample.spring.dao.iface.CountryDAO

package cz.vavru.sample.spring.dao.iface;

public interface CountryDAO {
        public cz.vavru.sample.spring.business.vo.Country get(java.lang.Integer key);
}

Použití property editorů ve Spring Frameworku

Ve Springu jsou property editory použity ve dvou základních scénářích.

  1. vytváření bean v aplikačním kontextu
  2. parsování a převod parametrů z requestu do na vlastnosti z objektu daného CommandControlleru

Vytváření bean v aplikačním kontextu

Náš property editor je nejdříve nutné registrovat v aplikačním kontextu. Jinak by se Spring Framework nedozvěděl, že pro editaci dané třídy má použít náš property editor…i když nemám tak docela pravdu. Pokud dodržíte konvenci, která říká, že property editor musí být ve stejném packagi jako editovaná třída a musí se jmenovat stejně jako editovaná třída se suffixem Editor, tak jej explicitně registrovat nemusíte. Explicitní registrace vypadá následovně:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
        ...some beans

        <bean id="customEditorConfigurer"
                class="org.springframework.beans.factory.config.CustomEditorConfigurer">
                <property name="customEditors">
                        <map>
                                <entry key="java.util.Currency">
                                        <ref bean="currencyPropertyEditor" />
                                </entry>
                                             <entry key="cz.vavru.sample.spring.business.vo.Country">
                                        <ref bean="countryPropertyEditor" />
                                </entry>
                        </map>
                </property>
        </bean>

           <bean id="currencyPropertyEditor"
                class="cz.vavru.sample.spring.propertyeditor.CurrencyPropertyEditor">
        </bean>

        <bean id="countryPropertyEditor"
                class="cz.vavru.sample.spring.propertyeditor.CountryPropertyEditor">
                <property name="countryDAO" ref="countryDAO" />
        </bean>

           ...some beans

Je potřeba pamatovat na to, že beana customEditorCon­figurer je platná pouze v kontextu daného souboru aplikačního kontextu. Neboli – je potřeba ji v každém souboru našeho aplikačního kontextu, ve kterém chceme mít naše PropertyEditory k dispozici, znovu explicitně deklarovat.

A nyní již k samotnému vytváření bean, které uvedené property editory budou používat. Opět může dojít k několika způsobům použití:

Nastavení vlastnosti beany typu java.util.Curren­cy (případně cz.vavru.sample­.spring.busines­s.vo.Country):

<bean id="myCompany" class="cz.vavru.sample.spring.business.vo.Country">
        <property name="currency" value="CZK" />
        <property name="country" value="1" />
</bean>

Uvedený kód vykoná to samé, jako kdybychom přímo v java kód napsali následující:

import cz.vavru.sample.spring.business.vo.Country;
import cz.vavru.sample.spring.business.vo.Company;

CountryDAO countryDAO = ...
Company comp = new Company();
comp.setCountry(countryDAO.get(1));
comp.setCurrency(new java.util.Currency.getInstance("CZK"));

Dalším případem může být vytvoření kolekce objektů požadovaného typu:

<bean id="myBean" class="...">
        <property name="currencies">
             <list>
                  <value type="java.util.Currency">CZK</value>
                  <value type="java.util.Currency">USD</value>
                        ...
                   </list>
              </property>
        <property name="countries">
             <list>
                  <value type="cz.vavru.sample.spring.business.vo.Country">1</value>
                  <value type="cz.vavru.sample.spring.business.vo.Country">2</value>
                        ...
                   </list>
              </property>
</bean>

A nakonec vytvoření java.util.Array­Listu v aplikačním kontextu:

<bean id="myBean" class="...">
        <property name="countries" ref="currencies" />
</bean>

<bean id="currencies"
        class="org.springframework.beans.factory.config.ListFactoryBean">
        <property name="sourceList">
                <list>
                       <value type="java.util.Currency">CZK</value>
                       <value type="java.util.Currency">USD</value>
                             ...
                </list>
        </property>
</bean>

Použití property editorů v CommandContro­lleru

Scénář je při editaci objektů zhruba takový:

  1. vytvoření referenční dat pro objekt/controller
  2. vytvoření formObjectu
  3. nabindování formObjectu objektu na JSP stránku (zde se volá metoda getAsText())
  4. odeslání stránky ke klientovi
  5. přijmutí updatnutých dat od klienta
  6. převod parametrů na objekty daných typů pomocí definovaných property editorů (zde se volá setAsText(String text)) a nabindování na formObject controller
  7. validace formObjectu
  8. uložení dat

Pro registraci našich property editorů v potomcích AbstractComman­dControlleru je nutné přetížit metodu initBinder. Controller pro editaci našeho objektu cz.vavru.sample­.spring.busines­s.vo.Company by mohl vypadat následovně:

package cz.vavru.sample.spring.web.controller;

import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.servlet.mvc.SimpleFormController;
import cz.vavru.sample.spring.propertyeditor.CountryPropertyEditor;
import cz.vavru.sample.spring.propertyeditor.CurrencyPropertyEditor;

public class CompanyController extends SimpleFormController {
        private CountryPropertyEditor countryPropertyEditor;
        private CurrencyPropertyEditor currencyPropertyEditor;

           ...getters/setters

        protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder)
                        throws ServletException {
                binder.registerCustomEditor(Country.class, countryPropertyEditor);
                binder.registerCustomEditor(java.util.Currency.class, currencyPropertyEditor);
        }
           ...
}

A pro zajímavost ještě fragment JSP stránky, která se stará o editaci:

<spring:nestedPath path="form">
        <form method="post" action="">
        <table cellpadding="0" cellspacing="0">
                <tr>
                        <th>Country:</th>
                        <td><spring:bind path="country">
                                <select name="<c:out value="${status.expression}"/>">
                                        <option value="0">Select country</option>
                                        <c:forEach var="_country" items="${countries}">
                                                <option
                                                        <c:if test="${_country.id == form.country.id}">selected</c:if>
                                                        value="<c:out value="${_country.id}"/>"><c:out
                                                        value="${_country.name}" /></option>
                                        </c:forEach>
                                </select></td>
                </tr>
                <tr>
                        <th>Coop currency:</th>
                        <td><spring:bind path="currency">
                                <select name="<c:out value="${status.expression}"/>">
                                        <option value="0">Select currency</option>
                                        <c:forEach var="_currency" items="${currencies}">
                                                <optionm
                                                        <c:if test="${_currency.currencyCode == form.currency.currencyCode}">selected</c:if>
                                                        value="<c:out value="${_currency.currencyCode}"/>">
                                                <c:out value="${_currency.currencyCode}" />
                                                </option>
                                        </c:forEach>
                                </select></td>
                </tr>
                <tr>
                        <td colspan="2"><input type="submit" name="save" value="Save" /></td>
                </tr>
        </table>
        </form>
</spring:nestedPath>

Závěr

Ještě bych chtěl dodat, že ve Spring Frameworku je již spousta property editorů hotových. A některé z nich dokonce defaultně registrovány. Nacházejí se v packagi org.springfra­mework.beans.pro­pertyeditors a zde je jejich výčet:

Odkazy

Článek patří do kategorie: Java, Spring Framework

1 Komentář Přidat komentář

Přidat komentář

Povinné

Povinné, skryté

Security Image Povinné
Opište text z obrázku

Povolené HTML značky:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>

Odkazovat na tento článek  |  Přihlásit se k odběru těchto komentářů přes RSS Feed


Kalendář

March 2024
M T W T F S S
« Jan    
 123
45678910
11121314151617
18192021222324
25262728293031

Poslední články

Locations of visitors to this page