Java validation with dynamic proxiesDecouple validation processes from your business object implementations ![]() | ![]() |
![]() |
Level: Introductory Eric Olson (eric.olson@lakeviewtech.com), Software Engineer, Lakeview Technologies 14 Sep 2004 Version 1.3 of the Java platform saw the introduction of the dynamic proxy facility. Dynamic proxies offer many interesting solutions to Java developers, including a validation scheme that easily decouples validation logic from an application's core business logic. In this article, Java developer Eric Olson shows you how dynamic proxies can keep your core application code free of validation routines and focused solely on business logic. Validation is an essential aspect of many enterprise applications. Most business methods contain validation logic to ensure that pre-conditions are met before carrying out business logic. Code that deals with values entered through a user interface employs validation logic to ensure that the values entered by a user are valid before carrying out actions that may affect other areas of the application or other users. Validation is an especially important component of applications that employ and interact with other loosely coupled components, as well as services that may not be strict in their assertions. As important as it is to the safety and functionality of your business applications, core application logic is often cluttered with validation routines. Validation processes are often scattered throughout method calls, making it difficult to tell the difference between validation logic and core business logic. In most cases, business objects and methods must know some details of the validation process and deal with them directly in their implementation -- for example, a business object may throw a validation exception directly from the business method (either coded directly in the method, or as a result of calling some validation service). The validation exception in this case is really a byproduct of the validation process, however, and would ideally be hidden from the business object implementation. In this article, I'll show you a more decoupled and centralized approach to validation, using the dynamic proxy facility introduced to the Java platform with version 1.3. Working with a single example throughout the article, I'll demonstrate the weaknesses of both tightly coupled and loosely coupled validation schemes, and then show you how dynamic proxies can help you improve on both. Throughout this article, I'll work with a simple Listing 1. User and its methods
In a tightly coupled data validation scheme, I would insert validation code directly into the interface's method implementations, as shown in Listing 2. Note that the validation logic is hardcoded into the method before the instance variable is set. Listing 2. A tightly coupled validation scheme
In this example, the validation logic is tightly coupled to the object in which it's being used. The weaknesses of this approach should be fairly obvious:
Although not ideal, tightly coupled validation code is quite common, particularly in older applications. Fortunately, tight coupling isn't the only option when it comes to coding the
You can get around tight coupling by having the interface implementation call a separate service to perform its validation logic . Typically, this service will have a set of validation rules assigned to specific methods on specific objects. Because the validation rules are decoupled from the interface's business logic, they can be reused across many methods on many objects. You can also define the validation rules externally, in which case changing the validation logic will be a matter of changing the validation service's configuration. Listing 3 shows how a validation service can be used to decouple validation logic from the implementation's core business logic. Listing 3. Using a validation service
Here, the validation logic is run as part of a service external to the object that is calling it. Specifically, validation performed on the
Although a step in the right direction, this approach still has some drawbacks. When I developed the method, I had to be sure to call the validation service and ensure that the method implementation declared its
Dynamic proxies are classes that implement a set of interfaces specified at runtime. Method invocations on a proxy class are dispatched to an invocation handler that is specified when the proxy class is generated. Dynamic proxy classes have many interesting uses within an application, one of which is to effectively handle pre- and post-method invocation operations in a uniform fashion. Because validation is often a pre-method invocation operation, dynamic proxies provide us with a solution to the problems outlined in the previous examples. Dynamic proxy classes give us a way to easily handle validation on any method in a uniform way, while completely separating all of the validation logic from the core business logic. Because interfaces already exist for the main business objects and services in many frameworks, you've likely had experience with swapping in and out different implementations of such interfaces. Using dynamic proxy classes is very similar, but instead of dealing directly with an implementation of the interface, clients deal with a proxy class that implements the interface, performs the desired validation, and delegates method calls through to an implementation class. With the dynamic proxy approach, all validation logic should be transparent to the clients of the proxied classes. As a result, implementing the new validation scheme should be fairly simple: I won't need to change a single line of the code that uses the
Conceptually, I'll create a custom invocation handler class that performs the validation rules. The invocation handler will contain an instance of a real implementation class as an instance variable. It will first validate method parameters upon a method invocation and then delegate the method call to the implementation class. When the application needs a business object instance, it will actually receive an instance of the dynamic proxy class. As you'll see in a moment, this allows the business object implementation class to remain completely independent of any code specific to the validation processes.
The invocation handler class is where all of the data validation logic is handled. The invocation handler class will also delegate method calls to a real implementation class in order to process the core business logic. Listing 4 shows an invocation handler that is not tied to any specific business object, and could therefore be used along with any business object that needs to be validated. Notice how the validation code in the Listing 4. The invocation handler
As you can see, this implementation of the invocation handler makes use of a generic validator service, the same as in Listing 3. For an alternative solution, I could have created an invocation handler that looks more like the one from Listing 2, where the validation code is run directly in the invocation handler. In this case, I would have used the invocation handler itself to check whether the method called was the
The business object implementation The next step is to specify a business object implementation to the constructor of the invocation handler. For the purpose of this example I'll use a validation-free implementation of the Listing 5. The validation-free User implementation
If you compare the
I can tie all this together with one final piece of code, which will actually create the dynamic proxy classes and attach the correct invocation handler to it. The simplest approach is to encapsulate the creation of the proxy class within the Factory pattern. Many business object frameworks employee a Factory pattern to create concrete implementations of their business object interfaces, such as the Listing 6. The UserFactory
Notice that the
Unfortunately, using dynamic proxy classes does have one major drawback: performance. Method invocations on dynamic proxy classes do not perform nearly as well a direct method call on objects. Therefore, your use of dynamic proxies in an application framework depends on what is more important to you: cleaner architecture or better performance. In many areas of an application, the performance drawback may be worth it, while in other areas performance is critical. One solution, therefore, is to use dynamic proxies in some areas and not in others. If you decide to go this route, keep in mind that the invocation handler can perform other operations besides validation, allowing you to change the behavior of your business objects at runtime or after code has been deployed.
Other uses for dynamic proxies Dynamic proxy classes have many uses within a business object framework other than simply validating methods in a uniform way. The simple invocation handler I built could be used for any pre- and post-method invocation processing. For example, I could easily take business method timings by inserting code before and after the business object implementation method calls to measure elapsed time. I could also notify interested listeners about state changes by inserting some post-method invocation logic.
In the example, I created the dynamic proxy class for a single interface: You should also notice in the example that I always call an actual implementation class to perform the business logic. This does not have to be the case. For example, the proxy class could delegate the method invocation to any other object or even handle it itself. A simple example of this is that I have an interface that exposes a number of JavaBeans properties via set/get methods. I also create a specialized invocation handler that holds on to a
Using dynamic proxy classes for validation is a simple and effective way to decouple validation routines from your application's core business logic. Unlike a tightly coupled approach, using dynamic proxies leaves you with validation code that is reusable and configurable. In this article, you saw the benefits of using dynamic proxies with an invocation handler. Because the method invocations on the dynamic proxy classes were all channeled through a common invocation handler, you could very easily change the logic performed by the handler, even in already deployed code or dynamically at runtime. The invocation handler could also be refactored to handle other operations across multiple method invocations on different object types. The Java platform's dynamic proxy facility is not your only option for decoupling validation routines from business logic in your core code. In some cases, such as where performance is the most important factor in the application, it may not even be the best option. While this article focused on dynamic proxies, I did discuss some other options, including the JavaBeans constrained properties facility and the use of code generation tools. As with any technology, you should carefully evaluate the alternatives and use dynamic proxies only when they are the best solution for your application.
|