如何使用Java动态代理?

  • Post category:Java

当我们需要在程序运行时为一个已有的Java类动态地添加某些功能或是拦截某些方法调用时,我们就可以使用Java动态代理来实现。以下是一份详细的Java动态代理使用攻略。

1. 动态代理基本原理

Java动态代理是通过利用Java反射机制,在运行期生成一个代理对象,实现对目标对象的代理访问,达到增强目标对象的功能或控制目标对象的访问方式的目的。每个代理类都必须实现 InvocationHandler 接口,并且每个代理类的实例都关联到了实现了该接口的InvocationHandler对象,它通过invoke()方法把对代理类的调用委托到该代理类关联的InvocationHandler对象的invoke()方法中。

2. 实现动态代理的步骤

  1. 创建一个实现InvocationHandler接口的类,该类必须实现invoke方法。
  2. 创建被代理的类以及接口。
  3. 通过Proxy类的静态方法newProxyInstance()动态生成代理类的实例。
  4. 通过代理类来调用目标方法。

3. 示例说明

3.1 代理操作

假设我们需要为一个接口的实现类增加打印调用方法的功能。我们首先创建一个接口IHelloWorld和一个实现类HelloWorld

public interface IHelloWorld {
  void sayHello();
}

public class HelloWorld implements IHelloWorld {
  public void sayHello() {
    System.out.println("Hello world!");
  }
}

接下来我们需要创建一个实现了InvocationHandler的代理类HelloWorldProxy,实现代理方法,当客户端调用代理对象的方法时,会被自动的调用如下代码中的invoke()方法。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class HelloWorldProxy implements InvocationHandler {
    private Object target; // 被代理的对象

    public HelloWorldProxy(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Called "+method.getName());
        Object result = method.invoke(target, args);
        return result;
    }
}

最后我们使用Proxy的静态方法newProxyInstance()HelloWorld类生成一个代理类proxy

public class Main {
    public static void main(String[] args) {
        HelloWorld helloWorld = new HelloWorld();
        HelloWorldProxy proxy = new HelloWorldProxy(helloWorld);
        IHelloWorld proxyInstance = (IHelloWorld) Proxy.newProxyInstance(
          helloWorld.getClass().getClassLoader(),
          helloWorld.getClass().getInterfaces(),
          proxy
        );

        proxyInstance.sayHello();
    }
}

以上代码中,我们首先创建了一个HelloWorld类的实例,在创建代理类的时候将该实例作为代理类的构造参数传入,最后用IHelloWorld类型引用代理对象,在调用相应方法时会调用HelloWorldProxy中的invoke()方法。

3.2 拦截操作

创建一个被拦截的类RealSubject。因为它是拦截对象,它必须实现一个接口Subject

interface Subject {
    void doSomething();
}

class RealSubject implements Subject {
    @Override
    public void doSomething() {
        System.out.println("RealSubject: doSomething");
    }
}

创建一个代理类DynamicProxy,它持有一个被代理的对象Subject的引用,并在被代理的方法执行前后做额外的操作。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

class DynamicProxy implements InvocationHandler {

    private Subject subject;

    public DynamicProxy(Subject subject) {
        this.subject = subject;
    }

    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        before();
        Object result = method.invoke(subject, args);
        after();
        return result;
    }

    private void before() {
        System.out.println("Before Method invoke");
    }

    private void after() {
        System.out.println("After Method invoke");
    }
}

使用Java动态代理模式实现拦截RealSubject类的执行。

public class Main {
    public static void main(String[] args) {
        Subject real = new RealSubject();
        Subject proxy = (Subject) Proxy.newProxyInstance(
          RealSubject.class.getClassLoader(),
          RealSubject.class.getInterfaces(),
          new DynamicProxy(real)
        );
        proxy.doSomething();
    }
}

在这个例子中,用户使用proxy代表real调用doSomething,实际上调用的却是DynamicProxyinvoke方法,在该方法里通过反射机制将调用转给被代理的RealSubject对象,从而实现了拦截和代理的功能。

4. 总结

Java动态代理模式是一种非常实用的代理技术,相比于静态代理来说,更加灵活。使用它可以在程序运行时动态地为某个对象添加某些功能或控制它的访问方式,避免在程序不断增加功能时对已有代码进行修改。理解Java动态代理模式的原理并掌握使用技巧,对程序员的职业发展大有裨益。