Thornton Rose
Published as "JavaBean Proxies", 8/13/2001, Gamelan.com.
Copyright © 2001, Thornton Rose.
While developing a small database application for keeping a work journal, I discovered I needed a way to map JavaBean properties to JTable columns. This led to creation of a component for accessing JavaBean properties by name at runtime -- in other words, a JavaBean proxy. In this article, I illustrate my simple JavaBean proxy and show how JavaBean proxies can be created with the new dynamic proxy classes in JDK 1.3.
To use the code from this article, you will need the following tools:
(Note: For the simple bean proxy code, you can actually use JDK 1.2. You only need JDK 1.3 for the dynamic bean proxy code.)
In order to access a JavaBean -- or any java class for that matter -- dynamically at runtime, you first need to get information about the capabilities of the class, or "class meta data". This information can come from two sources: BeanInfo classes and low-level analysis of the bean class. BeanInfo classes -- extensions of java.beans.SimpleBeanInfo or implementations of java.beans.BeanInfo -- provide descriptors of the properties, events, and methods of a class. They are associated with a class by the simple naming convention class name + "BeanInfo".
Here are some examples of beans and BeanInfo:
To get class metadata, you use can use introspection and/or reflection. Introspection is done with java.beans.Introspector, which will provide a java.beans.BeanInfo object that describes the properties, events, and methods of the class. Reflection is done with java.lang.Class, which will provide objects that let you access fields and methods of the class.
ClassInfo illustrates using introspection and reflection. It loads a given class, gets the class metadata, then prints the properties, events, and methods that are exposed by the class. Try it on Quark and Photon.
Once you have the metadata for a class, you can access instances of that class.
Here is an example of dynamically creating an instance of Quark and invoking its spin()
method:
// Load class.
Class quarkClass = Class.forName("Quark");
// Create instance.
Object quark = quarkClass.newInstance();
// Get spin() method.
Method spinMethod = quarkClass.getMethod("spin", new Class[] {});
// Invoke spin() method.
spinMethod.invoke(quark, new Object[] {});
BeanProxy is a reusable component that implements the mechanism shown above and acts as a proxy for instances of a given class. It provides the following operations:
BeanProxy(Object bean)
-- Create a BeanProxy for the given bean.BeanProxy(Class beanClass)
-- Create a BeanProxy for an instance of the given bean class.setBean(Object theBean)
-- Set the target bean.Object getBean()
-- Get the target bean.Object get(String name)
-- Get the value of the named bean property.void set(String name, Object value)
-- Set the named bean property to the given value.Object invoke(String name, Class[] types, Object[] parameters)
-- Invoke the method that has the given name and accepts the given types, passing it the given parameters.Technically, BeanProxy does not need the set()
and get()
methods, but I added them so that using a proxy object would be similar to using the target object. Here is an example of the differences in property access:
// Set property "color" on real object.
aPhoton.setColor("green");
// Set property "color" via proxy set() method.
aPhotonProxy.set("color", "green");
// Set property "color" via proxy invoke() method.
aPhotonProxy.invoke("setColor", new Class[] { String.class },
new Object[] { "green" });
Here is an example of using BeanProxy to create a proxy for an instance of Quark and invoking some methods on it:
// Load class.
Class beanClass = Class.forName("Quark");
// Create proxy for instance.
BeanProxy proxy = new BeanProxy(beanClass.newInstance());
// Call methods.
proxy.set("color", "strawberry");
proxy.invoke("spin", new Object[] {});
Dynamic proxy classes are one of the new features of JDK 1.3. A dynamic proxy provides access to a target object via interfaces that are specified at runtime and implemented by the proxy. When methods of these interfaces are invoked, they are dispatched to an invocation handler -- a class that implements java.lang.reflect.InvocationHandler -- which handles method invocations on the target object. This design gives dynamic proxies the following advantages over BeanProxy:
However, dynamic proxy classes also have some disadvantages (at least in comparison to BeanProxy):
The class java.lang.reflect.Proxy is used to create dynamic proxies. QuarkTrace is an example of using it to create a dynamic proxy that adds method call tracing to any class that implements IQuark. Try it on Quark and AntiQuark.
Can BeanProxy be implemented as a dynamic proxy? Yes. Here is one way:
Here is the code:
Here is an example of using DynamicBeanProxy to create a proxy for an instance of AntiQuark and invoking some methods on it:
// Load class.
Class beanClass = Class.forName("AntiQuark");
// Create proxy for instance.
IBeanProxy proxy = new DynamicBeanProxy(beanClass.newInstance());
// Call methods.
proxy.set("color", "black");
proxy.invoke("spin", new Object[] {});
So why would you want to use object proxies? Here are some things you can do with them: