Skip to content

bspies/proxy4j

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

37 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Build Status

Introduction

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.

Getting Started

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.

Proxy Styles

Virtual Proxies

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.

Invocation Handlers

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 {
        //...
      }
    }

Interceptors

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

Proxy Implementations

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.

Custom Classloaders

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);

Dependencies

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.

Using With Maven

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>

Known Issues

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.

About

Flexible proxying for Java. (This project has been moved from code.google.com/p/proxy4j.)

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages