Building Classes Dynamically with Java 2

Building Classes Dynamically with Java 2 

By Jean-Cedric Desrochers, OCI Software Engineer

October 2000


One of the new additions to the Java 2 Standard Edition (J2SE) version 1.3 release earlier this year is the Dynamic Proxy API. This mechanism allows programs to dynamically create bytecode that represents classes and create new instances of those classes.

A dynamic proxy class is a class that implements a list of interfaces specified at runtime. A method invocation through an interface implemented by the dynamic proxy will be encoded and dispatched to another object through a uniform interface.

This article introduces the specifications of the new Dynamic Proxy API and explains how to create a proxy class and where it should be used.

The Dynamic Proxy API

The concept of dynamically creating classes was first introduced in a Swing Connection article (link no longer available) in which the author discussed creating event listeners on the fly with a GenericListener class that created the bytecode of the class.

This functionality was reworked and brought into the J2SE 1.3. Modification was needed first to extend the concept to more than just event listeners. Also, modification was needed due to the security features of J2SE 1.3 that prevent applications from creating new classes in core Java packages.

Figure 1: UML Class Diagram of the Dynamic Proxy API

Fig 1 - UML Class Diagram of the Dynamic Proxy API

The API to create proxy classes can be found in the java.lang.reflect package and consists of only two classes and one interface.

Creation of a Proxy Class

To create a proxy class we need to use the getProxyClass() static method of the Proxy class. This method takes as an input argument a ClassLoader to load the created bytecode into the Java Virtual Machine (JVM) and an array of Class objects that represent the interfaces implemented by the new proxy class.

There are some restrictions on the parameters that can be passed to the method:

The result of the getProxyClass() method call is a Class object that extends the Proxy class and implements all the interfaces passed in.

The proxy class created is a public final class, and its unqualified name starts with $Proxy. If one of the interfaces implemented by the proxy class is not public, the proxy class will be generated in the same package as that interface. The created class will be cached, so that it can be reused to create additional objects that support the same list of interfaces.

Creation of a Proxy Instance

Now that we have a class that represents our proxy, let's create an instance of that proxy class.

Each proxy class is generated with one public constructor that takes as argument an InvocationHandler object. This object is the focal point of all the method invocations that are made on the proxy instance.

To create an instance, we can use reflection or the convenience static method newProxyInstance() of the Proxy class. This convenience method takes as an argument a ClassLoader, the list of interfaces to implement, and the InvocationHandler of the proxy instance. It will create the new proxy class if needed and will return a new proxy instance.

Method Invocation on a Proxy Instance

Now that we have an instance of our proxy, let's call methods on it.

Every method call on the proxy will be encoded and dispatched by the proxy itself to the InvocationHandler of the proxy. The invoke()method of the handler will be called with the following arguments:

The return value of the invoke() method will become the return value of the proxy instance. If an exception is thrown by the invoke() method, it will also be thrown by the proxy instance. If the type of the exception is not declared as a checked exception by the interface method, or if it's not an unchecked RuntimeException, the proxy instance will throw an UndeclaredThrowableException.

Example

To show the dynamic proxy in action, we'll look at a simple Swing application that allows the user to change the title of the frame.

The demo consists of a text field in which the user can enter the new title name of the frame and two buttons: one to set the title to the typed text and one to clear the text of the text field. We'll use a proxy instance to manage all the events that could occur in the frame.

Here's a list of the listeners used and their functionality in the demo:

The first step is to create a proxy class that implements all the listeners that we need. Next, we create an instance of that proxy class. We perform those two operations by using the Proxy.newProxyInstance() method:

  1. ...
  2. // Create the dynamic proxy instance
  3. aListenerProxy = (Proxy) Proxy.newProxyInstance(
  4. getClass().getClassLoader(),
  5. new Class[] { WindowListener.class,
  6. ActionListener.class,
  7. FocusListener.class,
  8. DocumentListener.class },
  9. this);
  10. ...

In order to create the proxy instance, we need to pass an InvocationHandler object that will receive all the method invocations. For convenience in our example, the demo class implements that interface.

Once we have the proxy instance, we can then add the proxy as the listener to our Swing objects:

  1. ...
  2. // Adding the proxy on the Swing components
  3. addWindowListener((WindowListener) aListenerProxy);
  4. btnOk.addActionListener((ActionListener) aListenerProxy);
  5. btnClear.addActionListener((ActionListener) aListenerProxy);
  6. txfName.addFocusListener((FocusListener) aListenerProxy);
  7. txfName.getDocument().addDocumentListener((DocumentListener) aListenerProxy);
  8. ...

Now that everything is hooked up, we just put the code in the invoke() method to implement the behavior we want.

Here's the source code of the invoke() method:

  1. /**
  2.   * Implementation of the InvocationHandler interface.
  3.   */
  4. public Object invoke(Object aProxy, Method aMethod, Object[] someArguments) {
  5. // If a method invocation was done on the WindowListener interface
  6. if (aMethod.getDeclaringClass() == WindowListener.class) {
  7. // Close the demo and exit the VM
  8. if (aMethod.getName().equals("windowClosing")) {
  9. System.exit(0);
  10. }
  11.  
  12. // If a method invocation was done on the ActionListener interface
  13. } else if (aMethod.getDeclaringClass() == ActionListener.class) {
  14. // Perform the action of the buttons
  15. ActionEvent anEvent = (ActionEvent) someArguments[0];
  16. if (anEvent.getSource() == btnOk) {
  17. setTitle(txfName.getText());
  18. } else {
  19. txfName.setText("");
  20. }
  21.  
  22. // If a method invocation was done on the FocusListener interface
  23. } else if (aMethod.getDeclaringClass() == FocusListener.class) {
  24. // Set the background color in yellow if the text field has the focus
  25. if (aMethod.getName().equals("focusGained")) {
  26. txfName.setBackground(Color.yellow);
  27. } else {
  28. txfName.setBackground(Color.white);
  29. }
  30.  
  31. // If a method invocation was done on the DocumentListener interface
  32. } else if (aMethod.getDeclaringClass() == DocumentListener.class) {
  33. // Enable / Disable the clear button
  34. if (txfName.getText().equals("")) {
  35. btnClear.setEnabled(false);
  36. } else {
  37. btnClear.setEnabled(true);
  38. }
  39. }
  40.  
  41. return null;
  42. }

Why use dynamic classes?

This concept of dynamically creating classes that implement a list of interfaces is increasingly popular, but why and where should we use it?

Definitively, creating Class objects at runtime and using reflection to dispatch the method invocation is more CPU intensive and slower; we can't deny it.

But on the other hand, the runtime generation of the class provides an alternative to reading .class files from the file system or from a remote server. It also provides the possibility of having a single point of method dispatching, the InvocationHandler interface.

The first benefit can be used in an applet that utilizes RMI where the time to download the classes is critical.

To talk to the server, the client needs the stubs of all the services it will use and the interfaces that are implemented by those stubs. With the Dynamic Proxy API, we can create a proxy class that implements the interfaces of all the services used by the applet and the InvocationHandler can serialize and send the method invocations to the server side. That way the applet doesn't need to load the stub.

The second benefit of the Dynamic Proxy can be explained with a Swing application. To add behavior to GUI Swing controls, we add listeners (ActionListener, KeyListener, DocumentListener, etc.) as anonymous inner classes. The problem is that those inner classes tend to consume more class file space than they should.

Most of the time those anonymous inner classes delegate their execution to another method. One way to avoid those inner classes is to use a dynamic proxy class. This approach simulates the inner classes that are there only to act as a pass-through to the method that contains the code to be executed. It avoids downloading all the anonymous classes in the case of an applet. It also allows tools that load classes dynamically (like IDEs) to generate event binding for listener interfaces that are not known until runtime.



Software Engineering Tech Trends (SETT) is a regular publication featuring emerging trends in software engineering.


secret