JDK动态代理入门

124 阅读3分钟

什么是代理(静态代理)

假设这样一个场景:

其他人(团队)写好的一个Java类,我们需要拿来使用,但发现该类并不能完全实现我们需要的功能,由于我们得到的是该类的字节码文件,无法对源码进行修改来扩展类的方法,此时就可以考虑代理的方法。

首先考虑静态代理。

//目标类
public class UtilClass implements UtilClassInterface {

    public void method1(){

    }
}
//代理类
public class ProxyUtilClass implements UtilClassInterface {

   public void method2(){
       UtilClass util=new UtilClass();
       util.method1();
       System.out.println("strengthen method");
   } 
}

在代理类中我创建了UtilClass的对象,调用其方法,但在方法调用后我自己添加的一句代码System.out.println("strengthen method"); 实现了原方法的功能增强。这就是代理的作用。

在B类无法直接访问A类时,可以通过A类的代理来间接访问A类。

静态代理的缺点

代码难以复用。

考虑以下情景: 目标类UtilClass和代理类都实现了接口UtilClassInterface,如果接口发生改变,目标类和代理类的代码都需要修改。另一方面一个目标类可能有多个代理类,那么接口的改变,其实现类的修改将是成倍的工作量。

什么是动态代理

使用JDK的反射机制创建代理类的对象。“动态”一方面指的是在程序运行时调用JDK的方法才能创建对象,另一方面指被代理的目标类可以根据传参来改变,而不是固定写好的Java代理类。

Tips: JDK代理的目标类必须是接口的实现类。

动态代理的实现

  • 创建目标类
public interface UtilClassInterface {
    void method();
}

public class Target implements UtilClassInterface{
    public void method(){
        System.out.println("target method");
    }
}
  • 创建InvocationHandler接口的实现类Handler,重写invoke方法
public class Handler implements InvocationHandler {
    Object target=null;
    //通过构造方法给目标对象赋值
    public Handler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //反射访问目标方法
        method.invoke(target,args);
        //方法增强
        System.out.println("strengthen method");
        //返回目标方法的返回值
        return null;
    }
}
  • 第三方类创建代理对象调用目标方法
public class Test {
    public static void main(String[] args) {
        //创建目标对象
        Target target=new Target();
        //创建Handler对象
        Handler handler=new Handler(target);
        //创建代理
        //第一个参数是指定代理类的类加载器(我们传入target的类加载器)
        //第二个参数是代理类需要实现的接口(我们传入被代理类实现的接口,
        //这样生成的代理类和被代理类就实现了相同的接口)
        //第三个参数是invocation handler,用来处理方法的调用。
        //这里传入我们自己实现的handler
        UtilClassInterface proxy=(UtilClassInterface) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            handler
        );
        //代理对象执行增强后的方法
         proxy.method();
    }
}

使用动态代理之后,我们不需要写代理类的Java文件就可以创建代理对象,并且代理的目标类由传入Handler构造方法的参数决定。当目标类的接口发生改变时,只需要修改目标类即可。