JavaEnterprise JavaJavaBean Proxies

JavaBean Proxies

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

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.

Tools

To use the code from this article, you will need the following tools:

  • The Java Development Kit (JDK), version 1.3.
  • Your favorite text editor.

(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.)

Bean Interrogation

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:

  • Quark is a simple JavaBean (no BeanInfo class).
  • Photon is a simple JavaBean that has a BeanInfo class.
  • PhotonBeanInfo is the BeanInfo class for Photon.

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.

Simple Bean Proxy

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 Bean Proxy

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:

  • Proxy objects are type-safe.
  • Using the proxy is like using the target object.
  • Method invocations are dispatched through a uniform interface.
  • Method calls can be intercepted and augmented.

However, dynamic proxy classes also have some disadvantages (at least in
comparison to BeanProxy):

  • An interface must be created for the target object.
  • Properties cannot be specified by name.
  • The proxy itself cannot expose methods.

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:

  1. Create an interface (i.e., an actual Java interface) for the public methods of BeanProxy.
  2. Create a factory class for creating the proxies.
  3. Implement an IBeanProxy and an InvocationHandler.

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[] {});

Uses

So why would you want to use object proxies? Here are some things you
can do with them:

  • Access bean properties by name (as a string) at runtime, — e.g., mapping
    JTable columns or database fields to bean properties.
  • Augment the behavior of an object at runtime — e.g., adding method call
    tracing.
  • Create a broker object that delegates to instances of other classes
    that may not be known until runtime.

Resources

About the Author

Thornton Rose is a contract
software developer and writer living in Atlanta, Ga.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories