Proxy4J is designed for creating proxies in Java with a high degree of flexibility, both in the implementation of the proxying itself as well as in the style of the proxy. Currently Proxy4J supports the following proxy styles:
- Virtual Proxies. Also known as delegation proxies, these proxies simply pass the method invocation directly to the "real subject", which may have some indirection in how it is created or fetched (e.g. the classic lazy-loading proxy).
- Invocation Handlers. These are a direct parallel to proxies in the JDK, with a slight twist to include type safety in the common case where only one interface is being proxied. Method calls on these proxies are passed to an invocation handler which decides how to handle the call.
- Interceptors. These are proxies where one or more interceptors may get called prior to invoking the method on the "real subject". In Proxy4J interceptors are very granular and may be specified on a per-method basis.
The centerpiece of the Proxy4J library is the ProxyFactory interface for creating proxies. So the first step in creating a proxy is to create an instance of an implementation for this interface:
ProxyFactory proxyFactory = new CglibProxyFactory();
Of course, you may also use dependency injection to specify the desired implementation.
ProxyFactory proxyFactory = Injector.create(ProxyFactory.class);
Then, to create the desired proxy, simply invoke the desired createProxy() method on the ProxyFactory, or use the buildInterceptor() fluent API to create a custom method interceptor.
For example, creating a lazily-loaded proxy is as easy as creating a virtual proxy by passing in the appropriate LazyProvider:
Foo fooProxy = proxyFactory.createProxy(Foo.class, new LazyProvider<Foo>() {
@Override protected Foo init() {
return new FooImpl();
}
});
The LazyProvider also uses double-checked locking to provide thread safety.
Virtual proxies in Proxy4J use the javax.inject.Provider interface to provide a level of indirection in fetching or creating the "real subject" of the proxy. Use of this interface also has the added benefit of allowing the reuse of existing dependency injection bindings in Spring or Guice. As shown in the example above, the canonical use case for virtual proxies is lazy loading.
As stated above, invocation handlers in Proxy4J mirror the JDK proxy implementation. They provide the largest degree of freedom in deciding how to handle a method invocation, as well as the ability to have no "real subject" at all.
In most cases, you will only want to proxy a single class/interface so you can use the type-safe version:
Foo fooProxy = proxyFactory.createProxy(Foo.class, new FooProxyHandler());
...where FooProxyHandler is an implementation of the ProxyHandler interface:
public class FooProxyHandler implements ProxyHandler<Foo> {
public Object handle(ProxyInvocation<Foo> invocation) throws Throwable {
//...
}
}
Method interceptors in Proxy4J are both powerful and flexible, as they allow multiple interceptors to be defined on a method as well as the specification of different interceptors for different methods on a given class.
If we define the class Foo as follows,
public class AcceptDoMethodFilter implements MethodFilter {
public boolean accept(Method method) {
return method.getName().startsWith("do");
}
}
We can now intercept any method on Foo that starts with "do" by writing the following:
Foo fooProxy = proxyFactory.buildInterceptor(Foo.class)
.on(new FooImpl())
.using(new AcceptDoMethodFilter(), new MyInterceptor())
.create();
Here the MethodFilter is defined as follows:
public class AcceptDoMethodFilter implements MethodFilter {
public boolean accept(Method method) {
return method.getName().startsWith("do");
}
}
The second argument to using() here is a variable-length argument that can take multiple implementations of MethodInterceptor. A variation of the using() method allows you to use a InterceptorFactory to generate the interceptors instead.
You can also use method annotations instead of the method filter to specify which methods should be proxied:
Foo fooProxy = proxyFactory.buildInterceptor(Foo.class)
.on(new FooImpl())
.using(MyAnnotation.class, new MyInterceptor())
.create();
Proxy4J currently supports the following implementations:
- CGLIB is a byte-code manipulation library built on top of ASM. It is supported in Proxy4J through the CglibProxyFactory implementation.
- Javassist is a Java runtime compiler and bytecode manipulation library. It is supported in Proxy4J through the JavassistProxyFactory implementation.
- The JDK. Though not as fast as byte code manipulation, the reflection-based JDK implementation of proxying has the advantage of no additional dependencies and compatibility with environments where byte code manipulation is either not desired or disallowed altogether. It is supported in Proxy4J through the JdkProxyFactory implementation.
All implementations of ProxyFactory extend the abstract base class BaseProxyFactory, which allows you to specify a preferred ClassLoader. This is specified as a constructor argument in each concrete implementation of ProxyFactory.
There is also a qualifying annotation, @ProxyLoader, that allows you to automatically inject this ClassLoader when using JSR-330 compliant dependency injection:
binder.bind(ClassLoader.class)
.annotatedWith(ProxyLoader.class)
.toInstance(myBridgeClassLoader);
Proxy4J has a hard dependency on both the aopalliance and jsr-330 jars. Using other dependencies depends on which implementation of ProxyFactory you are using.
Your Maven pom.xml will look something like this:
<dependencies>
<dependency>
<groupId>com.googlecode.proxy4j</groupId>
<artifactId>proxy4j-${module}</artifactId>
<version>${proxy4j.version}</version>
</dependency>
<!-- Only need if using CglibProxyFactory -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>${cglib.version}</version>
</dependency>
<!-- Only need if using JavassistProxyFactory -->
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>${javassist.version}</version>
</dependency>
</dependencies>
Here ${cglib.version} and ${javassist.version} will generally be the most recent versions of those libraries.
Proxy4J-Core is currently available from the Sonatype OSS releases repository.
<dependency>
<groupId>com.googlecode.proxy4j</groupId>
<artifactId>proxy4j-core</artifactId>
<version>1.1.0</version>
</dependency>
Using the CGLib-based proxy implementation will give warnings like the following when used with newer JDK versions (9+):
WARNING: Illegal reflective access by net.sf.cglib.core.ReflectUtils$1
CGLib will not work at all if used with the --illegal-access=deny
flag set.