Usefulness of java dynamic proxies vs regular proxies
I need some advice to which scenarios a dynamic proxy would prove more useful than a regular proxy.
I've put alot of effort into learning how to use dynamic proxies effectively. In this question, set aside that frameworks like AspectJ can perform basically everything we try to achieve with dynamic proxies, or that e.g., CGLIB can be used to address some of the shortcomings of dynamic proxies.
Use cases
- Decorators - e.g., perform logging on method invocation, or cache return values of complex operations
- Uphold contract - That is, making sure parameters are within accepted range and return types conform to accepted values.
- Adapter - Saw some clever article somewhere describing how this is useful. I rarely come across this design pattern though.
Are the others?
Dynamic proxy advantages
- Decorator: Log all method invocations, e.g.,
public Object invoke(Object target, Method method, Object[] arguments) {
System.out.println("before method " + method.getName());
return method.invoke(obj, args);
}
}
The decorator pattern is definately useful as it allows side effects to all proxies methods (although this behaviour is a book-example of using aspects ..).
- Contract: In contrast to a regular proxy, we need not implement the full interface. E.g.,
public Object invoke(Object target, Method method, Object[] arguments) {
if ("getValues".equals(method.getName()) {
// check or transform parameters and/or return types开发者_开发百科, e.g.,
return RangeUtils.validateResponse( method.invoke(obj, args) );
}
if ("getVersion".equals(method.getName()) {
// another example with no delegation
return 3;
}
}
The contract on the other hand only gives the benefit of avoiding the need to implement a complete interface. Then again, refactoring proxied methods would silently invalidate the dynamic proxy.
Conclusion
So what I see here is one real use case, and one questionable use case. What's your opinion?
There are a number of potential uses for dynamic proxies beyond what you've described -
- Event publishing - on method x(), transparently call y() or send message z.
- Transaction management (for db connections or other transactional ops)
- Thread management - thread out expensive operations transparently.
- Performance tracking - timing operations checked by a CountdownLatch, for example.
- Connection management - thinking of APIs like Salesforce's Enterprise API that require clients of their service to start a session before executing any operations.
- Changing method parameters - in case you want to pass default values for nulls, if that's your sort of thing.
Those are just a few options in addition to validation and logging like you've described above. FWIW, JSR 303, a bean validation specification, has an AOP-style implementation in Hibernate Validator, so you don't need to implement it for your data objects specifically. Spring framework also has validation built in and has really nice integration with AspectJ for some of the stuff described here.
Indeed AOP benefits most of the dynamic proxies. That's because you can create a dynamic proxy around an object that you don't know in advance.
Another useful side of a dynamic proxy is when you want to apply the same operation to all methods. With a static proxy you'd need a lot of duplicated code (on each proxied method you would need the same call to a method, and then delegate to the proxied object), and a dynamic proxy would minimize this.
Also note that Adapter and Decorator are separate patterns. They look like the Proxy pattern in the way they are implemented (by object composition), but they serve a different purpose:
- the decorator pattern allows you to have multiple concrete decorators, thus adding functionality at runtime
- the adapter pattern is meant to adapt an object to an unmatching interface. The best example I can think of is the
EnumetationIterator
- it adapts anEnumeration
to theIterator
interface.
Another use case I can think of is to dynamically implement interfaces at runtime, which is the way some frameworks work.
Take for instance Retrofit, a Java library for consuming REST services. You define a Java interface that reflects the operations available in the REST API, and decorate the methods with annotations to configure specifics of the request. It's easy to see that in this case all methods defined in the interface must execute a HTTP request against some server, transform the method arguments into request parameters; and then parse the response into a java object defined as the method return type.
精彩评论