Back

(java 往日的翻译) common-beanuti - 第三章, Dynamic Beans

发布时间: 2008-05-06 03:01:00

3. Dynamic Beans (DynaBeans) 动态Bean
3.1 Background 背景

The PropertyUtils class described in the preceding section is designed to provide dynamic property access on existing JavaBean classes, without modifying them in any way. A different use case for dynamic property access is when you wish to represent a dynamically calculated set of property values as a JavaBean, but without having to actually write a Java class to represent these properties. Besides the effort savings in not having to create and maintain a separate Java class, this ability also means you can deal with situations where the set of properties you care about is determined dynamically (think of representing the result set of an SQL select as a set of JavaBeans ...).

前面章节描述的PropertyUtils类是为了在不修改现有JavaBean的基础上,提供动态的属性访问。一个不同的动态属性访问用例是:当我们想根据动态的计算属性值来表示一个JavaBean,却又不想实际的写出一个java 类来表示这些属性。另外 effort savings(工作成就的储蓄?)不在于不得不建立和维护一个单独的java类,这个特性也意味着我们可以处理这样的情况:某个集合是动态确定的。(考虑如何使用JavaBean来表示一个select SQL语句的结果集……)


To support this use case, the BeanUtils package provides the DynaBean interface, which must be implemented by a bean class actually implementing the interface's methods, and the associated DynaClass interface that defines the set of properties supported by a particular group of DynaBeans, in much the same way that java.lang.Class defines the set of properties supported by all instances of a particular JavaBean class.

为了支持这个用例,BeanUtils包提供了DynaBean接口,这个接口必须被一个类实现,这个类还要实现相关的DynaClass的接口,该接口定义了一组被DynaBean所支持的属性。就跟 java.lang.Class 定义了一组被所有JavaBean的实例所支持的属性一样。


例如,上面例子中的Employee类就应该实现成一个DynaBean,而不是一个标准的JavaBean. 我们可以这样访问它的属性:


DynaBean employee = ...; // Details depend on which
// DynaBean implementation you use
String firstName = (String) employee.get("firstName");
Address homeAddress = (Address) employee.get("address", "home");
Object subordinate = employee.get("subordinate", 2);

One very important convenience feature should be noted: the PropertyUtils property getter and setter methods understand how to access properties in DynaBeans. Therefore, if the bean you pass as the first argument to, say, PropertyUtils.getSimpleProperty() is really a DynaBean implementation, the call will get converted to the appropriate DynaBean getter method transparently. Thus, you can base your application's dynamic property access totally on the PropertyUtils APIs, if you wish, and use them to access either standard JavaBeans or DynaBeans without having to care ahead of time how a particular bean is implemented.

一个非常有用的特性是:PropertyUtils的getter/setter方法知道如何访问DynaBean中的属性。因此,如果你把 bean作为第一个参数,也就是说, PropertyUtils.getSimpleProperty()是一个DynaBean的实现, 那么函数的调用就会显式的把getter方法(的返回值)转换成合适的类型。


Because DynaBean and DynaClass are interfaces, they may be implemented multiple times, in different ways, to address different usage scenarios. The following subsections describe the implementations that are provided as a part of the standard BeanUtils package, although you are encouraged to provide your own custom implementations for cases where the standard implementations are not sufficient.

因为DynaBean和DynaClass是接口,所以它们可能会被多次已不同的方式实现,来满足不同的需求。下面的小节描述了BeanUtils标准包所提供的部分实现,我们也鼓励您在它们不满足要求时,提供自己的实现。


3.2 BasicDynaBean and BasicDynaClass BasicDynaBean和 BasicDynaClass

The BasicDynaBean and BasicDynaClass implementation provides a basic set of dynamic property capabilities where you want to dynamically define the set of properties (described by instances of DynaProperty). You start by defining the DynaClass that establishes the set of properties you care about:
当我们想动态的(通过DynaProperty的实例描述)定义一组属性时,BasicDynaBean和BasicDynaClass提供了一个基本的支持动态属性的集合
DynaProperty[] props = new DynaProperty[]{
new DynaProperty("address", java.util.Map.class),
new DynaProperty("subordinate", mypackage.Employee[].class),
new DynaProperty("firstName", String.class),
new DynaProperty("lastName", String.class)
};
BasicDynaClass dynaClass = new BasicDynaClass("employee", null, props);

Note that the 'dynaBeanClass' argument (in the constructor of BasicDynaClass) can have the value of null. In this case, the value of dynaClass.getDynaBeanClass will just be the Class for BasicDynaBean.

注意,在BasicDynaClass的构造函数中,参数'dynaBeanClass'可以是null。在该情况下,dynaClass.getDynaBeanClass的值 就是BasicDynaBean.

Next, you use the newInstance() method of this DynaClass to create new DynaBean instances that conform to this DynaClass, and populate its initial property values (much as you would instantiate a new standard JavaBean and then call its property setters):
然后,你可以使用该DynaClass的 newInstantce()方法来建立新的DynaBean实例,然后初始化它的属性值。(就跟我们初始化标准JavaBean时调用它的setter方法一样)


DynaBean employee = dynaClass.newInstance();
employee.set("address", new HashMap());
employee.set("subordinate", new mypackage.Employee[0]);
employee.set("firstName", "Fred");
employee.set("lastName", "Flintstone");

Note that the DynaBean class was declared to be DynaBean instead of BasicDynaBean. In general, if you are using DynaBeans, you will not want to care about the actual implementation class that is being used -- you only care about declaring that it is a DynaBean so that you can use the DynaBean APIs.
请注意,则里的DynaBean被声明为DynaBean而不是BasicDynaBean。一般而言,我们使用DynaBean时,不必考虑它使用的是哪个实现——只需要知道它是一个DynaBean,可以让我们使用DynaBean API就行。


As stated above, you can pass a DynaBean instance as the first argument to a PropertyUtils method that gets and sets properties, and it will be interpreted as you expect -- the dynamic properties of the DynaBean will be retrieved or modified, instead of underlying properties on the actual BasicDynaBean implementation class.

如上所述,我们可以把一个DynaBean的实例作为第一参数传递到PropertyUtils的方法,来取得或设置属性。结果跟我们期待的一样——DynaBean的(而不是BasicDynaBean的)动态属性会被查询或修改,

3.3 ResultSetDynaClass (Wraps ResultSet in DynaBeans) 在DynaBean中封装ResultSet

A very common use case for DynaBean APIs is to wrap other collections of "stuff" that do not normally present themselves as JavaBeans. One of the most common collections that would be nice to wrap is the java.sql.ResultSet that is returned when you ask a JDBC driver to perform a SQL SELECT statement. Commons BeanUtils offers a standard mechanism for making each row of the result set visible as a DynaBean, which you can utilize as shown in this example:
DynaBean API的一个很常见的用法,是对难于在JavaBean中表示的东东进行封装。
当我们使用JDBC运行一个SELECT语句时所返回的java.sql.ResultSet,就是可以被很漂亮的封装的常见集合之一。Commons BeanUtils提供了一个标准的装置来让每一条的结果已DynaBean的形式可见。我们可以象下面这样:

Connection conn = ...;
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery
("select account_id, name from customers");
Iterator rows = (new ResultSetDynaClass(rs)).iterator();
while (rows.hasNext()) {
DynaBean row = (DynaBean) rows.next();
System.out.println("Account number is " +
row.get("account_id") +
" and name is " + row.get("name"));
}
rs.close();
stmt.close();

3.4 RowSetDynaClass (Disconnected ResultSet as DynaBeans)

Although ResultSetDynaClass is a very useful technique for representing the results of an SQL query as a series of DynaBeans, an important problem is that the underlying ResultSet must remain open throughout the period of time that the rows are being processed by your application. This hinders the ability to use ResultSetDynaClass as a means of communicating information from the model layer to the view layer in a model-view-controller architecture such as that provided by the Struts Framework, because there is no easy mechanism to assure that the result set is finally closed (and the underlying Connection returned to its connection pool, if you are using one).
尽管ResultSetDynaClass是使用一连串的DynaBean来表示某个SQL查询结果的有用技术,但一个重要问题是,底层的ResultSet必须在每一行被处理的整个过程中保持open状态。如果是在一个MVC架构中,例如Struts框架,就会阻碍信息从model 层被传送到view层,原因在于没有简单的东西来保证结果集被最终关闭了(如果使用连接池的话,也不能保证底层的connection被释放到连接池中)。


The RowSetDynaClass class represents a different approach to this problem. When you construct such an instance, the underlying data is copied into a set of in-memory DynaBeans that represent the result. The advantage of this technique, of course, is that you can immediately close the ResultSet (and the corresponding Statement), normally before you even process the actual data that was returned. The disadvantage, of course, is that you must pay the performance and memory costs of copying the result data, and the result data must fit entirely into available heap memory. For many environments (particularly in web applications), this tradeoff is usually quite beneficial.

RowSetDynaClass 提供了解决该问题的一种不同的方式。当你建立它的实例时,底层的数据就会被COPY到内存中的表示查询结果的一组DynaBean中。这个技术的优点在于,我们可以马上关闭ResultSet的连接(以及对应的Statement),通常在你处理实际的数据之前就已经关闭了。缺点在于,我们必须担负 COPY结果数据时的运行以及内存消耗,而且结果集必须全部的放到可用heap内存中。对于大部分的环境(尤其是web应用)来说,这种交换是非常值得的。


As an additional benefit, the RowSetDynaClass class is defined to implement java.io.Serializable, so that it (and the DynaBeans that correspond to each row of the result) can be conveniently serialized and deserialized (as long as the underlying column values are also Serializable). Thus, RowSetDynaClass represents a very convenient way to transmit the results of an SQL query to a remote Java-based client application (such as an applet).
作为一个附加的好处,RowSetDynaClass 是java.io.Serializable的实现,所以它(以及对应每行结果的DynaBean)可以被很方便的串行化与并行化。(只要底层的列的值也支持Serializable)。因此,RowSetDynaClass提供了一个很方便的方式,把SQL查询的结果集发送到基于java的客户端应用程序。(例如applet)


The normal usage pattern for a RowSetDynaClass will look something like this:
对于RowSetDynaClass的通常用法,是这样的:

Connection conn = ...; // Acquire connection from pool
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT ...");
RowSetDynaClass rsdc = new RowSetDynaClass(rs);
rs.close();
stmt.close();
...; // Return connection to pool
List rows = rsdc.getRows();
...; // Process the rows as desired

3.5 WrapDynaBean and WrapDynaClass

OK, you've tried the DynaBeans APIs and they are cool -- very simple get() and set() methods provide easy access to all of the dynamically defined simple, indexed, and mapped properties of your DynaBeans. You'd like to use the DynaBean APIs to access all of your beans, but you've got a bunch of existing standard JavaBeans classes to deal with as well. This is where the WrapDynaBean (and its associated WrapDynaClass) come into play. As the name implies, a WrapDynaBean is used to "wrap" the DynaBean APIs around an existing standard JavaBean class. To use it, simply create the wrapper like this:
OK,我们已经试过了DynaBean API,它们确实很COOL —— 简单的使用 get()和 set()方法就可以很容易的访问DynaBean中动态定义的simple, indexed, mapped属性。我们可以使用DynaBean API来访问所有的bean,但是我们却要处理一堆标准JavaBean。这里就是WrapDynaBean(以及关联的WrapDynaClass)的用武之地了!从名字就可以看出,WrapDynaBean是使用DynaBean API来“封装”标准JavaBean的。简单的建立一个wrapper(封装器)就可以使用它:

MyBean bean = ...;
DynaBean wrapper = new WrapDynaBean(bean);
String firstName = wrapper.get("firstName");

Note that, although appropriate WrapDynaClass instances are created internally, you never need to deal with them.
请注意,尽管WrapDynaClass的实例已经在内部被建立,但是你不需要去处理它们。——译注:没看明白。

3.6 Lazy DynaBeans

* 1. LazyDynaBean - A Lazy DynaBean
* 2. LazyDynaMap - A light weight DynaBean facade to a Map with lazy map/list processing
* 3. LazyDynaList - A lazy list for DynaBean's, java.util.Map's or POJO beans.
* 4. LazyDynaClass - A MutableDynaClass implementation.

You bought into the DynaBeans because it saves coding all those POJO JavaBeans but you're here because lazy caught your eye and wondered whats that about? What makes these flavors of DynaBean lazy are the following features:
你想bought into(了解?购买?认识?)DynaBeans 是因为它节省了编码的时间。但是你来到这里是因为"lazy"吸引了你的眼球,让你想知道这是什么东东。下面的特点让DaynaBean的lazy更有滋味:

* Lazy property addition - lazy beans use a DynaClass which implements the MutableDynaClass interface. This provides the ability to add and remove a DynaClass's properties. Lazy beans use this feature to automatically add a property which doesn't exist to the DynaClass when the set(name, value) method is called.
延迟属性增加 - lazy bean 使用了MutableDynaClass接口的实现类。所以它的特定是可以增加或删掉一个DynaClass的属性。 Lazy bean使用该特性可以在调用set()方法增加一个本不存在的属性时,增加该属性,并设置。

* Lazy List/Array growth - If an indexed property is not large enough to accomodate the index being set then the List or Array is automatically grown so that it is.
延迟链表/数组增长 - 如果一个 indexed 属性的长度不够用了,那么这个List或者 Array的长度就会自动增长,直到满足需求。

* Lazy List/Array instantiation - if an indexed property doesn't exist then calling the DynaBean's indexed property getter/setter methods (i.e. get(name, index) or set(name, index, value)) results in either a new List or Array being instantiated. If the indexed property has not been defined in the DynaClass then it is automatically added and a default List implementation instantiated.
延迟List/Array实例化 - 如果一个indexed属性不存在时调用DynaBean的 getter/setter方法(例如get(name, index) or set(name, index, value)),就可以建立一个新的实例化的List或Array. 如果DynaClass的indexed属性没有被修改,那么它就会自动增加并且实例化一个默认的List实现。

* Lazy Map instantiation - if a mapped property doesn't exist then calling the DynaBean's mapped property getter/setter methods (i.e. get(name, key) or set(name, key, value)) results in a new Map being instantiated. If the mapped property has not been defined in the DynaClass then it is automatically added and a default Map implementation instantiated.
延迟Map实例化- 同上。
* Lazy Bean instantiation - if a property is defined in the DynaClass as a DynaBean or regular bean and doesn't exist in the DynaBean then LazyDynaBean wiill try to instantiate the bean using a default empty constructor.
延迟Bean实例化 - 如果一个DynaClass的属性中定义了一个DynaBean 或者一般的Bean,但是它并不存在与DynaBean中,那么LayDynaBean就会使用默认的空的构造函数来初始化这个bean.
1. LazyDynaBean is the standard lazy bean implementation. By default it is associated with a LazyDynaClass which implements the MutableDynaClass interface - however it can be used with any MutableDynaClass implementation. The question is how do I use it? - well it can be as simple as creating a new bean and then calling the getters/setters...
1. LazyDynaBean是标准 lazy bean的实现。它默认是与实现了MutableDynaClass接口的LazyDynaClass相关联的,它也可以和任何 MutableDynaClass的实现类相关联。问题是我们如何使用它?-恩,其实象建立一个新bean,然后调用getter/setter方法一样简单...


DynaBean dynaBean = new LazyDynaBean();

dynaBean.set("foo", "bar"); // simple

dynaBean.set("customer", "title", "Mr"); // mapped
dynaBean.set("customer", "surname", "Smith"); // mapped

dynaBean.set("address", 0, addressLine1); // indexed
dynaBean.set("address", 1, addressLine2); // indexed
dynaBean.set("address", 2, addressLine3); // indexed

2. LazyDynaMap is a light wieght DynaBean facade to a Map with all the usual lazy features. Its light weight because it doesn't have an associated DynaClass containing all the properties. In fact it actually implements the DynaClass interface itself (and MutableDynaClass) and derives all the DynaClass information from the actual contents of the Map. A LazyDynaMap can be created around an existing Map or can instantiate its own Map. After any DynaBean processing has finished the Map can be retrieved and the DynaBean facade discarded.

LazyDynaMap是一个轻量级的DynaBean,面向具有一般意义上的延迟特点的Map. 它是轻量级的原因是它没有与包含了所有属性的DynaClass相关联。事实上呵实现了DynaClass接口(与MutableDynaClass)而且由Map中的DynaClass信息而来(?)一个LazyDynaMap可以从一个现有的Map创建,或者从它自己而来。任何DynaBean被处理之后,这个Map就可以被得到,这个 DynaBean就被废弃。

If you need a new Map then to use....
如果我们需要个新Map,可以这样...

DynaBean dynaBean = new LazyDynaMap(); // create DynaBean

dynaBean.set("foo", "bar"); // simple
dynaBean.set("customer", "title", "Mr"); // mapped
dynaBean.set("address", 0, addressLine1); // indexed

Map myMap = dynaBean.getMap() // retrieve the Map

or to use with an existing Map ....

Map myMap = .... // exisitng Map
DynaBean dynaBean = new LazyDynaMap(myMap); // wrap Map in DynaBean
dynaBean.set("foo", "bar"); // set properties

3. LazyDynaList is lazy list for DynaBean's, java.util.Map's or POJO beans. See the Javadoc for more details and example usage.
LazyDynaList是DynaBean,java.util.Map或POJO bean的延迟list.更多细节与例子请看Javadoc

4. LazyDynaClass extends BasicDynaClass and implements the MutableDynaClass interface. It can be used with other DynaBean implementations, but it is the default DynaClass used by LazyDynaBean. When using the LazyDynaBean there may be no need to have anything to do with the DynaClass However sometimes there is a requirement to set up the DynaClass first - perhaps to define the type of array for an indexed property, or if using the DynaBean in restricted mode (see note below) is required. Doing so is straight forward...
LazyDynaClass扩展了BasicDynaClass,实现了MutableDynaClass。它可以与其他DynaBean的实现一同使用,默认是LazyDynaBean的DynaClass(?)。当使用LazyDynaBean时或许根本用不上DynaClass,但是有时我们需要先建立起DynaClass - 也许是为了定义一个indexed的属性,也许是在restricted 模式(见下面的note)使用,如果你正在这么做的话请继续看...


Either create a LazyDynaClass first...
或者先建立一个LazyDynaClass ...

MutableDynaClass dynaClass = new LazyDynaClass(); // create DynaClass

dynaClass.add("amount", java.lang.Integer.class); // add property
dynaClass.add("orders", OrderBean[].class); // add indexed property
dynaClass.add("orders", java.util.TreeMapp.class); // add mapped property

DynaBean dynaBean = new LazyDynaBean(dynaClass); // Create DynaBean with associated DynaClass

or create a LazyDynaBean and get the DynaClass...
或者建立一个LazyDynaBean 然后取得DynaClass...

DynaBean dynaBean = new LazyDynaBean(); // Create LazyDynaBean
MutableDynaClass dynaClass =
(MutableDynaClass)dynaBean.getDynaClass(); // get DynaClass

dynaClass.add("amount", java.lang.Integer.class); // add property
dynaClass.add("myBeans", myPackage.MyBean[].class); // add 'array' indexed property
dynaClass.add("myMap", java.util.TreeMapp.class); // add mapped property


注意:MutableDynaClass的一个特点是具有Restricted属性。如果这个Dynaclass是restricted时,它的属性不能被增加或删掉,LazyDynaBean和LazyDynaMap也都不能自动的增加属性。

Back