{"id":2386,"date":"2013-09-28T12:29:46","date_gmt":"2013-09-28T10:29:46","guid":{"rendered":"http:\/\/www.tutego.de\/blog\/javainsel\/?p=2386"},"modified":"2013-09-28T12:37:25","modified_gmt":"2013-09-28T10:37:25","slug":"dynamisch-praktisch-gut-mit-dem-beanutils-properties-einfach-beherrschen","status":"publish","type":"post","link":"https:\/\/www.tutego.de\/blog\/javainsel\/2013\/09\/dynamisch-praktisch-gut-mit-dem-beanutils-properties-einfach-beherrschen\/","title":{"rendered":"Dynamisch. Praktisch. Gut Mit dem BeanUtils Properties einfach beherrschen"},"content":{"rendered":"<p>Die Java-Beans sind ein allgegenw\u00e4rtiges Gut. Zwar sind die Anforderungen minimal, aber f\u00fcr viele Bereiche wie grafische Oberfl\u00e4chen, JSPs, OR-Mappern sind sie nicht wegzudenken. Damit eine Bean zur Bean wird, muss eine Klasse<\/p>\n<ul>\n<li>\u00f6ffentlich sein <\/li>\n<li>einen \u00f6ffentlichen Standard-Konstruktur besitzen und <\/li>\n<li>\u00f6ffentliche Zugriffsfunktionen anbieten. <\/li>\n<\/ul>\n<p>Nach dieser Vorgabe ist Folgendes eine g\u00fcltige Bean:<\/p>\n<pre lang=\"java\">class Weihnachtsmann\n{\n  private String name;\n\n  public String getName() {\n    return name;\n  }\n\n  public void setName( String name ) {\n    this.name = name;\n  }\n}<\/pre>\n<p>Die Zugriffsfunktionen kapseln die internen Eigenschaften und sind nach einem festen Muster aufgebaut. F\u00fcr ein Attribut String name w\u00fcrden die beiden Methoden void setName(String name) und String getName() angelegt. F\u00fcr boolean-Eigenschaften ist die Vorsilbe is- \u00fcblich. Wenn die Beans nicht nach dem Schema aufgebaut sind, kann eine zus\u00e4tzliche BeanInfo-Beschreibung helfen.<\/p>\n<p>W\u00e4hrend auf der einen Seite die Bean als Datencontainer Einsatz findet, sind sie noch aus einen anderen Grund reizvoll: durch ihren festen Aufbau l\u00e4sst sich leicht mit Reflection\/Instrospection auf die Bean und ihre Eigenschaften zugreifen. Nicht umsonst beschreiben Bean grafische Komponenten, so dass ein GUI-Builder v\u00f6llig automatisch die Eigenschaften auslesen kann und Manipulationen erlaubt. Ein Exemplar einer Bean l\u00e4sst sich \u00fcber ihren Namen einfach anlegen; mit einem bekannten Class-Objekt liefert newInstance() ein neues Exemplar.<\/p>\n<pre lang=\"java\">Class beanClass = Class.forName( &quot;Weihnachtsmann&quot; );\nObject santa = beanClass.newInstance(); <\/pre>\n<p>F\u00fcr das Auslesen der Methodennamen definiert Java Introspection und f\u00fcr den Zugriff Reflection. Vor dem Zugriff auf die Bean-Properties sind jedoch die Pr\u00e4fixe get- und set- zu setzen und Datentypkonvertierungen durchzuf\u00fchren.<\/p>\n<p>Um den Umgang mit Bean-Eigenschaften zu vereinfachen, hat die Apache-Gruppe in ihren Commons-Paket die BeanUtils aufgenommen. Die aktuelle Version der BeanUtils ist 1.6.1 und ist beim Indianer-Server http:\/\/jakarta.apache.org\/commons\/beanutils\/ zuhause. BeanUtils setzt zwei weitere Pakete aus Commons voraus: Collections und Logging. Der prominenteste Nutzer der Bibliothek ist Struts.<\/p>\n<h3>Bean-Eigenschaften lesen und setzen<\/h3>\n<p>Eine Bean-Property kennzeichnet \u00e4hnlich wie ein Servlet\/JSP-Attribut zwei Merkmale: Einen Namen (immer ein String) mit einer Belegung (beliebiger Objekttyp). Um bei einer Bean eine Property zu setzen, m\u00fcssen ihr Name und die neue Belegung nat\u00fcrlich bekannt sein, beim Lesen nur der Name. Auslesen und Setzen von Eigenschaften erlauben zwei statische Funktionen der Utility-Klasse org.apache.commons.beanutils.PropertyUtils: PropertyUtils.getSimpleProperty(Object bean, String name) und PropertyUtils.setSimpleProperty( Object bean, String name, Object value). Um unserem Weihnachtsmann einen Namen zu geben &#8212; und diesen gleich wieder auszulesen &#8212; ist zu schreiben:<\/p>\n<pre lang=\"java\">PropertyUtils.setSimpleProperty( santa, &quot;name&quot;, &quot;Santa Clause&quot; );\nString name = (String) PropertyUtils.getSimpleProperty( santa, &quot;name&quot; );<\/pre>\n<p>Die Methoden sind jedoch nicht ohne ausf\u00fchrliche Fehlerbehandlung auszuf\u00fchren; auftreten kann eine IllegalAccessException, IllegalArgumentException (eine RuntimeException), InvocationTargetException und NoSuchMethodException.<\/p>\n<p>Zu bedenken ist, dass eine Bean-Property ganz spezielle Methoden erzwingt. Das geht so weit, dass BeanUtils nur genau eine get-Methode erwartet. Gibt es neben der erforderlichen get-Methode eine weitere, so liefert BeanUtils beim Zugriff einen Fehler. Der Zugriff auf bounds einer java.awt.Component gelingt daher nicht, da neben der parameterlosen Variante von getBound() auch getBounds(Rectange) existiert &#8212; ein Aufruf \u00e4hnlich wie der Folgende funktioniert daher nicht und f\u00fchrt zu einer NoSuchMethodException: PropertyUtils.getSimpleProperty(new Label(), &quot;bounds&quot;);<\/p>\n<h3>PropertyUtils, BeanUtils und Konvertierungen<\/h3>\n<p>Neben der Klasse PropertyUtils, die bisher Verwendung fand, gibt es noch zus\u00e4tzlich die Klasse BeanUtils. Sie erinnert von der Methodenvielfalt an PropertyUtils, doch sie f\u00fchrt f\u00fcr Datentypen m\u00f6gliche Konvertierungen durch. Das Beispiel zum Weihnachtsmann soll erweitert werden, so dass er ein Attribute int alter besitzt. Der Versuch, das Attribut mit einem Wert vom Datentyp String \u00fcber PropertyUtils.setProperty() zu setzen scheitert mit einer IllegalArgumentException. Ein int muss mit einem Integer-Objekt belegt werden. Wird jedoch anstelle der Klasse PropertyUtils die Klasse BeanUtils gesetzt, so funktioniert alles, denn BeanUtils f\u00fchrt die notwendigen Konvertierungen von String in int selbst aus.<\/p>\n<pre lang=\"java\">BeanUtils.setProperty( santa, &quot;alter&quot;, &quot;612&quot; );<\/pre>\n<p>Ergibt sich bei der Konvertierung ein Fehler, so macht das nichts, denn dann wird die Zuweisung nicht durchgef\u00fchrt. (Apropos Konvertierungen: Die Firma Lockheed, die die Raumsonde Mars Climate Orbiter in England konstruierte, nutze die englische Einheit Inch, die NASA in Amerika Zentimeter; beim Landeversuch crashte die Sonde 1999 auf den Mars.)<\/p>\n<p>Die Konvertierung aus einem String f\u00fcr die unterschiedlichsten Datentypen nimmt BeanUtils automatisch vor. Im oberen Fall findet eine Konvertierung von String in ein Integer Objekt statt. Die Converter selbst sind Klassen, die die Schnittstelle org.apache.commons.beanutils.Convert implementieren und auf diese Weise von einem String in BigDecimal, BigInteger, boolean, Boolean, byte, Byte, char, Character, Class, Double, float, Float, int, Integer, long, Long, short, Short, String, <\/p>\n<p>java.util.Date, java.sql.Date, java.sql.Time, java.sql.Timestamp und mehr konvertieren. Den passenden Konverter sucht ConvertUtils.lookup(). Falls also BeanUtils.setProperty() vor der Aufgabe steht, einen passenden Typ herauszufinden, so sucht es zuerst nach dem Typ (von alter w\u00e4re das int) und anschlie\u00dfend den passenden Konverter. Da ConvertUtils.lookup(int.class) den IntegerConverter liefert, darf er die Zeichenfolgen umwandeln. Diese Konvertierung ist die \u00fcbliche Konvertierung mit Integer.parseInt().<\/p>\n<p>Falls die Standard-Konverter einen Typ nicht abdeckt, kann mit register() ein neuer Konverter eingeh\u00e4ngt werden. Nicht n\u00f6tig ist das f\u00fcr sprachabh\u00e4ngige Konvertierungen, denn hier bietet das Paket schon Hilfe an. So gibt es den IntegerConverter in der lokalisierten Fassung auch als IntegerLocaleConverter. Zum Nachschlagen dient aber nicht mehr ConvertUtils, sondern LocaleConvertUtils. Die Basisklassen f\u00fcr lokalisierte eigene Konvertiert ist LocaleConverter. Es gibt f\u00fcr alle normalen Konverter auch lokalisierte Konverter. Das lokalisierte Parsen scheint allerdings noch nicht so ganz ausgereift.<\/p>\n<h3>Eine Liste aller Eigenschaften<\/h3>\n<p>Eine Belegung aller Eigenschaften liefert BeanUtils.describe() beziehungsweise PropertyUtils.describe().<\/p>\n<pre lang=\"java\">Map description = BeanUtils.describe( new JButton() );<\/pre>\n<p>Der Assoziativspeicher unter dem Namen description enth\u00e4lt als Schl\u00fcssel alle Properties und als Werte die Belegungen der Eigenschaften.<\/p>\n<pre lang=\"java\">verifyInputWhenFocusTarget=true\ndisplayedMnemonicIndex=-1\nwidth=0\n...<\/pre>\n<h3>Formular-Parameter \u00fcbertragen<\/h3>\n<p>In JSPs erfreut eine Schreibweise den Entwickler, die alle Parameterwerte eines Requests in eine Bean \u00fcbertr\u00e4gt. Das Tag &lt;jsp:setProperty&gt; wird dann mit dem Attribut property=&quot;*&quot; belegt.<\/p>\n<pre lang=\"xml\"><jsp:usebean id=\"bean\" ...><\/jsp:usebean>\n<jsp:setproperty property=\"*\" name=\"bean\"><\/jsp:setproperty><\/pre>\n<p>Diese M\u00f6glichkeit sieht zwar die JSP-Spezifikation vor, doch in Servlets fehlt diese M\u00f6glichkeit. Sie wird aber insbesondere f\u00fcr MVC-Frameworks interessant, in dem der Controller selbst\u00e4ndig die Formulardaten in eine Bean \u00fcbertr\u00e4gt. Das kann schnell nachimplementiert werden. Die Servlet-API bietet in ServletRequest die Funktion getParameterNames(), um mit einer Enumeration \u00fcber die Parameter zu laufen. F\u00fcr jeden der Parameter kann setProperty() auf einer passenden Bean aufgerufen werden.<\/p>\n<pre lang=\"java\">for ( Enumeration params = request.getParameterNames(); params.hasMoreElements(); ) {\n  String name = (String) params.next();\n  if ( name != null )\n    BeanUtils.setProperty( bean, name, request.getParameterValues(name) );\n}<\/pre>\n<p>getParameterValues() liefert ein Feld, was von der Bean auch verarbeitet werden muss. Sonst tut&#8217;s bei eindeutigen Parametern auch getParameter().<\/p>\n<h3>Werte einer Map in eine Bean kopieren<\/h3>\n<p>Aufgabe: Eine Ini-Datei enth\u00e4lt Schl\u00fcssel\/Werte-Paare und alle Schl\u00fcssel sollen auf Eigenschaften einer Bean \u00fcbertragen werden. F\u00fcr Schl\u00fcssel soll es also entsprechende set- und get-Methoden geben. Eine einfache Aufgabe mit den passenden Klassen. Zum Laden der Paar-Datei findet Properties und die Objektfunktion load() Einsatz. Ein Properties-Objekt ist eine spezialisierte Hashtable &#8212; eine dumme Designentscheidung Begr\u00fcndung ? &#8212;, und alle Schl\u00fcssel lassen sich aufz\u00e4hlen. An dieser Stelle kommen wieder die BeanUtils ins Spiel. Die statische Funktion setProperty() sieht jeden Schl\u00fcssel als Property und \u00fcbertr\u00e4gt den Wert auf die Bean.<\/p>\n<p>Unter der Annahme, dass alle Eigenschaften in einer Map vorliegen, bietet BeanUtils eine feine Funktion: static void populate(Object bean, Map properties). Die Map schreibt Schl\u00fcssel als String vor, die Werte sind beliebige Objekte. populate() iteriert \u00fcber die Schl\u00fcssel von properties und initialisiert \u00fcber viele setProperty()-Funktionen die Werte der Bean. Die Aufgabe l\u00f6st sich somit in wenigen Zeilen Quellcode.<\/p>\n<p>Die Funktion populate() lie\u00dfe sich auch bei Formular-Parametern verwenden, wenn denn die Schl\u00fcssel\/Werte-Paare in einer Map vorliegen w\u00fcrden. Doch das k\u00f6nnen sie so ohne weiteres nicht, denn ein Schl\u00fcssel darf durchaus mehrmals mit verschiedenen Werten vorkommen &#8212; eine Map kann das nicht unterst\u00fctzen. Gilt, dass \u00fcberladene Parameter nicht zu erwarten sind, k\u00f6nnen von Hand die Parameter in eine Map einsortiert werden, so dass doch wieder populate() nutzbar ist. Seit der Servlet 2.3 Spezifikation bietet HttpServletRequest die Methode getParameterMap(), was direkt eine Map liefert. Sie speichert die Werte jedoch immer als Felder; wenn der Schl\u00fcssel einmalig ist, dann ist das Feld nur ein Element gro\u00df.<\/p>\n<h3>Indexierte Properties<\/h3>\n<p>Da ein Weihnachtsmann immer Rentiere besitzt, soll ihn eine zus\u00e4tzliche Klasse Rentier f\u00fcr die Zugtiere bei der Arbeit unterst\u00fctzen. Er soll in einem internen Feld zugtier die Tiere speichern und daf\u00fcr auch zwei neue Zugriffsfunktionen definieren.<\/p>\n<pre lang=\"java\">class Weihnachtsmann\n{\n  private Rentier[] zugtier = new Rentier[8];\n  public Rentier getZugtier( int index ) {\n    return zugtier[index];\n  }\n  public void setZugtier( int index, Rentier zugtier ) {\n    this.zugtier[index] = zugtier;\n  }\n}<\/pre>\n<p>Ist ein Weihnachtsmann aufgebaut und mit setZugtier() ganz klassisch ein Rentier zugewiesen kann im n\u00e4chsten Schritt die Eigenschaft erfragt werden.<\/p>\n<pre lang=\"java\">Weihnachtsmann w = new Weihnachtsmann();\nw.setZugtier( 0, new Rentier() );\nRentier z = (Rentier) PropertyUtils.getIndexedProperty( w, &quot;zugtier&quot;, 0 );<\/pre>\n<p>Diese indexierten Eigenschaften lassen sich mit getIndexedProperty() erfragen und mit setIndexedProperty() setzen; die Signaturen sind PropertyUtils.getIndexedProperty(Object bean, String name, int index) und PropertyUtils.setIndexedProperty(Object bean, String name, int index, Object value).<\/p>\n<pre lang=\"java\">Rentier rudolph = new Rentier();\nPropertyUtils.setIndexedProperty( w, &quot;zugtier&quot;, 1, rudolph );<\/pre>\n<p>Eine Alternative zu den Funktionen, die den Index als Parameter erwarten ist eine Schreibweise, die den Index in den Eigenschaftennamen kodiert. Die Schreibweise ist bekannt von Arrays: name[index].<\/p>\n<pre lang=\"java\">Rentier zt = (Rentier) PropertyUtils.getIndexedProperty( w, &quot;zugtier[1]&quot; )<\/pre>\n<h3>Mapped Properties<\/h3>\n<p>F\u00fcr indexierte Properties ist es \u00fcblich, dass der Index eine positiver ganzzahliger Wert ist. Eine Erweiterung der Java-Beans gehen die BeanUtils mit den Mapped Properties, was indexierte Beans mit Schl\u00fcsseln sind &#8212; das Konzept erinnert an einen Assoziativspeicher. Genauso wie indexierte Properties mit eckigen Klammern einen Index aufnehmen, gibt es eine Abk\u00fcrzung auch bei den abgebildeten Eigenschaften. Dabei wird der Schl\u00fcssel in runden Klammern hinter die Eigenschaft gesetzt. Die Signaturen sind PropertyUtils.getMappedProperty(Object bean, String name) mit den alternativen Parametern (Object bean, String name, String key), (Object bean, String name, Object value) und (Object bean, String name, String key, Object value).<\/p>\n<p>Ein Beispiel: System.getProperties() liefert ein Properties mit den Systemeigenschaften. Die Objektmethode getProperty() liefert zu einem gegebenen Schl\u00fcssel den assoziierten Wert. BeanUtils l\u00e4sst mit getMappedProperty() die abgebildeten Eigenschaften erfragen.<\/p>\n<pre lang=\"java\">Properties props = System.getProperties();\nSystem.out.println( PropertyUtils.getMappedProperty( props, &quot;property(user.name)&quot; ) );<\/pre>\n<p>Wo ist der Vorteil gegen\u00fcber props.get(&quot;user.name&quot;) ;<\/p>\n<p>Der Schl\u00fcssel muss nicht in einfachen oder doppelten Anf\u00fchrungszeichen eingeschlossen werden, wie es etwa die Expression Language unter JSP 2.0 oder Velocity vorschreibt.<\/p>\n<h3>Geschachtelte Properties<\/h3>\n<p>Um eine Liste aller Zeichens\u00e4tze des Betriebssystems auszulesen, l\u00e4sst sich \u00fcber GraphicsEnvironment getLocalGraphicsEnvironment() aufrufen.<\/p>\n<pre lang=\"java\">GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();<\/pre>\n<p>Ausdruck Der Name des ersten Fonts erfragt<\/p>\n<pre lang=\"java\">String s = ge.getAllFonts()[0].getName();<\/pre>\n<p>Wie sieht das nun mit den BeanUtils aus? Sicherlich ist es denkbar, f\u00fcr den indexierten Property einmal getIndexedProperty() und f\u00fcr den Namen setSimpleProperty() aufzurufen, aber commons.beanutils kann es besser, und zwar mit getNestedProperty().<\/p>\n<pre lang=\"java\">Object o = PropertyUtils.getNestedProperty( ge, &quot;allFonts[0].name&quot; );<\/pre>\n<p>W\u00e4hrend mit getNestedProperty() eine Eigenschaft ausgelesen werden kann, l\u00e4sst sie sich mit setNestedProperty() setzen. Die Signaturen f\u00fcr set- und get sind: PropertyUtils.getNestedProperty(Object bean, String name) und PropertyUtils.setNestedProperty(Object bean, String name, Object value).<\/p>\n<p>Nun sind drei M\u00f6glichkeiten bekannt, auf Bean-Eigenschaften zuzugreifen: Einfache Properties (getSimpleProperty(), setSimpleProperty()), indexierte Properties (getIndexedProperty()\/setIndexedProperty()), mapped Properites und geschachtelte Properites (getNestedProperty()\/setNestedProperty()). Um den Umgang zu vereinfachen, hilft die allgemeine Funktion getProperty(), die sich den passenden Typ heraussucht. Damit l\u00e4sst sich alles mit nur einer Art von Funktion beschreiben: PropertyUtils.getProperty(Object bean, String name) und PropertyUtils.setProperty(Object bean, String name, Object value).<\/p>\n<h3>Kopien von Bean-Eigenschaften<\/h3>\n<p>Stehen zwei Beans gegen\u00fcber, die sich Properties teilen, so kopiert BeanUtils. copyProperties() die Eigenschaften. Die Klasse Point definiert genauso wie Rectangle das Property x und y. Die Belegungen der Eigenschaften lassen sich \u00fcbertragen, so dass das Rectangle alles annimmt, was in Point definiert wird:<\/p>\n<pre lang=\"java\">Point p = new Point( 10, 20 );\nRectangle rec = new Rectangle(); \nBeanUtils.copyProperties( rec \/* = *\/, p );\nSystem.out.println( rec ); \/\/ java.awt.Rectangle[x=10,y=20,width=0,height=0]<\/pre>\n<p>Intern geht copyProperties() alle Eigenschaften vom Punkt durch, findet x, y und auch location. F\u00fcr diese drei Eigenschaften werden die entsprechenden set-Methoden beim Rectangle-Objekt gesucht; sie werden nur f\u00fcr x und y gefunden, denn setLocation() ist \u00fcberladen und so ung\u00fcltig.<\/p>\n<p>Ein Beispiel, unter dem Bean-Attribute zu kopiert sind, liefert Struts. Die Action Form Beans werden zwar allzu oft als Model eingesetzt und weiterverarbeitet, das ist aber falsch. Die Form Beans sind lediglich eine Hilfs-Struktur, die nur f\u00fcr die Formulardaten eine Berechtigung haben. Demnach sind sie auch nur in einem Struts-Durchlauf bekannt und werden nicht als Datencontainer weitergegeben. Trennt man allerdings Form-Bean von tats\u00e4chlicher Model-Bean (falls eine Model-Bean ben\u00f6tigt wird) sieht das Model sehr \u00e4hnlich der Form-Bean aus und entsteht leider doppelter Quellcode, insbesondere f\u00fcr die set- und get-Methoden. Weiterhin ist l\u00e4stig, dass bei einer g\u00fcltigen Formularbelegung alle Form-Eigenschaften auf die Model-Bean \u00fcbertragen werden. Das artet aus zu einer Reihe von Aufrufen der Art<\/p>\n<pre lang=\"java\">modelBean.setXXX( formBean.getXXX() );<\/pre>\n<p>An diese Stellt helfen die BeanUtils und die praktische Funktion copyProperties(). Sie \u00fcbertr\u00e4gt automatisch die Eigenschaften der Form-Bean in die Model-Bean.<\/p>\n<pre lang=\"java\">BeanUtils.copyProperties( modelBean, formBean );<\/pre>\n<h3>Bean-Objekte klonen<\/h3>\n<p>Auf copyProperties() aufbauend lassen sich Kopien von Beans anlegen lassen. Da das Kopieren auf Grund der Eigenschaften und set\/get-Methoden geschieht, ist eine Implementierung der Schnittstelle Clonable nicht n\u00f6tig. Mit der clone()-Methode teilt cloneBean() jedoch mit, dass die Kopie wieder eine flache und keine tiefe ist: static Object cloneBean(Object bean).<\/p>\n<h3>Dynamische Beans<\/h3>\n<p>Bisher waren es existierende Beans, die \u00fcber wirkliche set- und get-Methoden verf\u00fcgten. Mit den BeanUtils lassen sich jedoch auch &quot;virtuelle&quot; Beans erzeugen, also Beans, die mit den BeanUtils verwaltet werden k\u00f6nnen, aber keine passenden set- und get-Funktionen besitzen. Diese Spezial-Beans implementieren die Schnittstelle DynaBean und verwalten \u00fcber diese Schnittstelle Schl\u00fcssel\/Werte-Paare. Genauso wie eine Klasse den Bauplan f\u00fcr Objekte beschreibt, beschreibt die Schnittstelle DynaClass den Bauplan f\u00fcr DynaBean-Ojekte. Exemplare der virtuellen Beans f\u00fchren zuerst \u00fcber die DynaClass. Die BeanUtils bringen eine Basisimplementierung in Form der Klasse BasicDynaClass mit; ihre Eigenschaften werden als Array von DynaProperty-Objekten \u00fcbergeben.<\/p>\n<pre lang=\"java\">DynaProperty[] dynaProp = new DynaProperty[] {\n  new DynaProperty( &quot;schenker&quot;, String[].class ),\n  new DynaProperty( &quot;bringer&quot;, Weihnachtsmann.class ),\n  new DynaProperty( &quot;volumen&quot;, int.class )\n};\n\nBasicDynaClass dynaClass = new BasicDynaClass( &quot;geschenk&quot;, null, dynaProp ); <\/pre>\n<p>Die BasicDynaClass beschreibt f\u00fcr ein Geschenk ein Feld von Schenkern (wie &quot;Mama&quot;, &quot;Papa&quot;), den Weihnachtsmann und die Gr\u00f6\u00dfe des Geschenks.<\/p>\n<p>Nach der Beschreibung der Klasse bildet die Objektfunktion newInstance() ein Exemplar der dynamischen Bean:<\/p>\n<pre lang=\"java\">DynaBean paket = dynaClass.newInstance();<\/pre>\n<p>Alternativ zu dieser Schreibweise l\u00e4sst sich ein Exemplar BasicDynaBean bilden, der im Konstruktor die beschreibende Klasse annimmt: paket = new BasicDynaBean(dynaClass);<\/p>\n<p>Diese DynaBean besitzt \u00fcber die Schnittstelle Methoden zum Erfragen, Setzen oder L\u00f6schen von Attributbelegungen. Die Methode set() setzt zum Beispiel eine Eigenschaft.<\/p>\n<pre lang=\"java\">paket.set( &quot;schenker&quot;, new String[1] );\npaket.set( &quot;bringer&quot;, new Weihnachtsmann() );<\/pre>\n<p>Die set()-Funktion ist \u00fcberladen, um auch indexierte und abgebildete Eigenschaften zu unterst\u00fctzten. Das gleiche gilt f\u00fcr die get-Funktion. W\u00e4hrend bei den herk\u00f6mmlichen Beans der Zugriff get&lt;Property&gt;() steht, so hei\u00dft der nun get(&quot;&lt;Property&gt;&quot;).<\/p>\n<pre lang=\"java\">System.out.println( paket.get(&quot;volumen&quot;) );<\/pre>\n<p>Da das BeanUtils-Paket bei den bekannten Funktionen wie PropertyUtils.getProperty() neben den &quot;normalen&quot; Beans auch die DynaBean unterst\u00fctzen, m\u00fcssen die \u00fcber die Schnittstelle DynaBean vorgeschriebenen Funktionen nicht verwendet werden.<\/p>\n<pre lang=\"java\">PropertyUtils.setSimpleProperty( paket, &quot;volumen&quot;, new Integer(123) );\nInteger volumen = (Integer) PropertyUtils.getSimpleProperty( paket, &quot;volumen&quot; );<\/pre>\n<p>Da im Beispiel vorher schon das Feld der Schenker mit einem String-Array der L\u00e4nge eins initialisiert wurde, l\u00e4sst sich ein konkreten Element einfach mit der allgemeinen Funktion setProperty() setzen.<\/p>\n<pre lang=\"java\">PropertyUtils.setProperty( paket, &quot;schenker[0]&quot;, &quot;Ulli&quot; );\nSystem.out.println( PropertyUtils.getProperty(paket, &quot;schenker[0]&quot;) ); \/\/ Ulli<\/pre>\n<h3>Mantel um existierende Beans<\/h3>\n<p>Die DynaBeans bietet einen einheitlichen Standard f\u00fcr einfache, indexierte oder abgebildeten Eigenschaften \u00fcber setXXX() und getXXX()-Funktionen. Um auch existierende Beans als DynaBeans zu verwaltet, sind die Klasse WrapDynaBean und WrapDynaClass definiert. Eine WrapDynaBean legt sich um eine bekannten Bean, so dass diese sich als DynaBean verwalten l\u00e4sst.<\/p>\n<pre lang=\"java\">Weihnachtsmann myWeih = new Weihnachtsmann();\nDynaBean wrapper = new WrapDynaBean( myWeih );\nwrapper.set( &quot;name&quot;, &quot;Schlittenheld&quot; );\nString nameW = (String) wrapper.get( &quot;name&quot; );\nSystem.out.println( &quot;Wrapper: &quot; + nameW + &quot;, Bean: &quot; + myWeih.getName() );\nwrapper.set( &quot;alter&quot;, new Integer(891) );\nint alterW = ((Integer) wrapper.get( &quot;alter&quot; )).intValue();\nSystem.out.println( &quot;Wrapper: &quot; + alterW + &quot;, Bean: &quot; + myWeih.getAlter() );<\/pre>\n<h3>DynaBeans in Struts *<\/h3>\n<p>In Struts f\u00fchrt die Implementierung der Form-Beans f\u00fcr die Abbildung der Formulardaten zu l\u00e4stigen Aufwand. Die DynaBeans helfen, diesen Aufwand zu minimieren, da keine wirkliche Bean-Klasse mehr zu implementieren ist, sondern die &quot;virtuelle&quot; Bean Verwendung findet. In der struts-config.xml wird zun\u00e4chst unter form-bean wie \u00fcblich die Bean eingetragen. Allerdings ist der Typ nun nicht der Form-Bean Typ, sondern DynaActionForm. In den folgenden Elementen der XML-Datei wird genau die schon bekannten DynaProperty f\u00fcr eine DynaClass eingetragen &#8212; nur nicht in Java, sondern in einer Konfigurationsdatei.<\/p>\n<pre lang=\"xml\">&lt;form-bean name=&quot;dynaWeihnachtsmannForm&quot;\n           type=&quot;org.apache.struts.action.DynaActionForm&quot;&gt;\n  &lt;form-property name=&quot;name&quot; type=&quot;java.lang.String&quot;\/&gt;\n  &lt;form-property name=&quot;alter&quot; type=&quot;java.lang.Integer&quot;\/&gt;\n&lt;\/form-bean&gt;<\/pre>\n<p>Da Struts mit den HTML-Tags die DynaBeans ber\u00fccksichtigt, muss an der View (JSP-Seite) keine Ver\u00e4nderung vorgenommen werden. Nur die Action muss eine Ver\u00e4nderung erfahren, da ja ohne DynaBeans das ActionForm aus der execute()-Funktion auf die konkrete Bean typangepasst w\u00fcrde, um die entsprechenden set- und get-Funktionen zu nutzen. Dieser Programmcode muss nun umgebaut werden. Statt dessen gibt Struts eine Klasse DynaActionForm vor, zu der eine Typanpassung vorgenommen wird. \u00dcber diese Klasse l\u00e4sst sich mit get- und set-Funktionen die Bean nutzen.<\/p>\n<pre lang=\"java\">public ActionForward execute(\n ActionMapping mapping, ActionForm form,\n HttpServletRequest request,\n HttpServletResponse response )\n  throws ServletException, IOException\n{\n  DynaActionForm weihForm = (DynaActionForm) form;\n  String name = weihForm.get( &quot;name&quot; );\n  ...<\/pre>\n<p>Nicht umsonst sieht der Zugriff auf die Attribute \u00fcber DynaActionForm so aus wie schon im vorangehenden Beispiel beschrieben: DynaActionForm implementiert die Schnittstelle org.apache.commons.beanutils.DynaBean.<\/p>\n<p>Die DynaActionForm ersetzt vollst\u00e4ndig die Form-Bean, wobei eine Sache auf der Streckte bleibt: die Validierung. F\u00fcr die \u00dcberpr\u00fcfung der Formularbelegungen wurde in der Unterklasse von ActionForm die Methode validate() \u00fcberschrieben. Bei den DynaBean gibt es aber keine eigene Bean-Klasse! Eine L\u00f6sung ist eine Validierung \u00fcber das Validator-Framework (auch eine Jakarta Commons), also \u00fcber eine XML-Datei, eine andere ist doch wieder eine Unterklasse. Diese spezielle Bean-Klasse leitet von der schon bekannten DynaActionForm ab und implementiert wie bekannt die validate()-Funktion. In struts-config.xml muss nat\u00fcrlich DynaActionForm wieder verschwinden und der Name der Unterklasse eingesetzt werden.<\/p>\n<h3>DynaBeans f\u00fcr Datenbanken<\/h3>\n<p>Die DynaBeans finden auch an anderer Stelle Verwendung: bei Datenbanken. Ein DynaBeans-Objekt kann eine Zeile eines ResultSet repr\u00e4sentieren. Um das zu erreichen, legt sich ein ResultSetDynaClass-Objekt um ein ergebnislieferndes ResultSet.<\/p>\n<pre lang=\"java\">ResultSetDynaClass rsdc = new ResultSetDynaClass( rs, true );<\/pre>\n<p>ResultSetDynaClass implementiert die Schnittstelle DynaClass. Das ist wichtig, denn DynaClass verwaltet die Beschreibung der Eigenschaften \u00fcber DynaProperty-Objekte. Bei der ResultSetDynaClass werden die Namen der Spalten zu den Namen der Eigenschaften. Der Konstruktor von ResultSetDynaClass ist \u00fcberladen und vertr\u00e4gt ein zus\u00e4tzliches boolean, damit die Namen der Eigenschaften nicht exakt in Gro\u00df- und Kleinbuchstaben \u00fcbereinstimmen m\u00fcssen. Ist die Belegung true, so reichen alle Namen in Kleinbuchstaben. Wichtiger ist aber die Funktion iterator() von ResultSetDynaClass, die der Reihe nach die Zeilen als DynaBean liefert; die Beschreibung der Klassen-Informationen war ja wichtiges Teil in den DynaProperty-Objekten.<\/p>\n<pre lang=\"java\">for ( Iterator rows = rsdc.iterator(); rows.hasNext(); ) {\n  DynaBean row = (DynaBean) rows.next();\n  System.out.println( row.get(&quot;bla&quot;) );\n}<\/pre>\n<p>Nach dem Durchlaufen kann das ResultSet geschlossen werden, das bedeutet, dass zur Zeit des Durchlaufens eine Verbindung zur Datenbank bestehen muss.<\/p>\n<p>Die Frage ist nun, was ein ResultSetDynaClass bringt; zum Durchlaufen tut es auch das ResultSet. Ein Vorteil ist, dass die Daten, die \u00fcber die DyaBean ver\u00f6ffentlicht werden, leicht auf andere Beans kopiert werden k\u00f6nnen; man erinnere sich an BeanUtils.copyProperties(ziel, quelle). Oftmals m\u00fcssen die Datenbankinhalte auf das Model einer Applikation \u00fcbertragen werden, so dass ein ResultSetDynaClass dazu gut geeignet ist.<\/p>\n<p>Eine Eigenschaft der Klasse BasicDynaBean (und BasicDynaClass) ist ihre Serialisierungs-M\u00f6glichkeit, da die Klasse die Schnittstelle java.io.Serializable implementiert, und somit persistent gemacht werden kann. Eine normale DynaBean implementiert diese Schnittstelle nicht, so dass man an dieser Stelle die Serialisierung nicht erzwingt. Aber nat\u00fcrlich kann eine andere Klasse DynaBean und Serializable gleichzeitig implementieren und das w\u00e4re sogar praktisch, denn k\u00f6nnte die Daten gleich abgespeichert werden. Wurde zum Beispiel eine BasicDynaBean angelegt, k\u00f6nnen Daten auf diese Bean \u00fcbertragen werden und sie l\u00e4sst sich sp\u00e4ter abspeichern. Es w\u00e4re doch praktisch, wenn auch die Zeilen vom ResultSetDynaClass serialisierbar w\u00e4ren. Dann w\u00e4re das Problem gel\u00f6st, wie sich Datenbankinhalte einfach \u00fcbertragen lassen &#8212; O.K., ein RowSet ist auch eine L\u00f6sung. Die Antwort aus den BeanUtils ist die Klasse RowSetDynaClass, die Serializable implementiert. RowSetDynaClass liest aus einem ResultSet alle Zeilen aus und speichert sie, so dass sie serialisierbar werden.<\/p>\n<pre lang=\"java\">RowSetDynaClass rsdc = new RowSetDynaClass( rs );<\/pre>\n<p>Nach dieser Zeile kann das ResultSet rs schon geschlossen werden. Der Zugriff auf die Zeilen geschieht anschlie\u00dfend mit getRows(), was eine java.util.List mit den bekannten DynaBean-Objekten liefert. Mit dem serialisier-f\u00e4higen RowSetDynaClass lassen sich die Inhalte \u00fcber RMI leicht transportieren, ohne dass viel Programmcode entwickelt werden muss.<\/p>\n<h3>Links<\/h3>\n<ul>\n<li><a href=\"https:\/\/commons.apache.org\/proper\/commons-beanutils\/\">https:\/\/commons.apache.org\/proper\/commons-beanutils\/<\/a> <\/li>\n<li><a href=\"https:\/\/commons.apache.org\/proper\/commons-beanutils\/javadocs\/v1.8.3\/apidocs\/index.html\">https:\/\/commons.apache.org\/proper\/commons-beanutils\/javadocs\/v1.8.3\/apidocs\/index.html<\/a> <\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Die Java-Beans sind ein allgegenw\u00e4rtiges Gut. Zwar sind die Anforderungen minimal, aber f\u00fcr viele Bereiche wie grafische Oberfl\u00e4chen, JSPs, OR-Mappern sind sie nicht wegzudenken. Damit eine Bean zur Bean wird, muss eine Klasse \u00f6ffentlich sein einen \u00f6ffentlichen Standard-Konstruktur besitzen und \u00f6ffentliche Zugriffsfunktionen anbieten. Nach dieser Vorgabe ist Folgendes eine g\u00fcltige Bean: class Weihnachtsmann { private [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":"","_links_to":"","_links_to_target":""},"categories":[4],"tags":[42],"class_list":["post-2386","post","type-post","status-publish","format-standard","hentry","category-open-source","tag-apache"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/2386","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/comments?post=2386"}],"version-history":[{"count":4,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/2386\/revisions"}],"predecessor-version":[{"id":2391,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/2386\/revisions\/2391"}],"wp:attachment":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/media?parent=2386"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/categories?post=2386"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/tags?post=2386"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}