Java中的动态代理

发布于:2024-06-12 ⋅ 阅读:(56) ⋅ 点赞:(0)

动态代理

动态代理的时候,定义一个接口,需要代理人和被代理类实现这个接口,这样不够灵活,代理类能够代理的类只有实现这个接口的类。非常不灵活,假如被代理人的类没有实现这个接口,那么就需重新写一个代理类。对于日志、事务这些操作是不区分业务的,即不需要规定都实现某接口。因此,出现了动态代理

java种的动态代理生成的方式大概有三种JDK动态代理、instrument动态代理、cglib动态代理。其中,前两种是JDK自带的,cglib是需要第三方依赖使用的。

  1. JDK动态代理:在程序运行的过程种动态的生成代理类,这个代理类实现被代理类的接口,因此使用时候被代理类必须实现接口
  2. instrument动态代理:是通过Class字节文件在加载至内存的过程种添加拦截器,动态的修改字节文件实现的;
  3. cglib:在程序运行的过程种动态生成代理类,这个代理类是通过继承被代理类实现的,因此不能代理被final修饰的类

JDK动态代理和cglib动态代理的底层都是ASM

JDK动态代理

先看这样一个程序

/** 接口*/
public interface Mobile {
    void move();
}
/** 被代理类*/
public class Car implements Mobile{
    @Override
    public void move() {
        System.out.println("move...");
    }
}
/** 使用动态代理*/
public class Test {
    public static void main(String[] args) {
        Car car = new Car();
        System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles",true);
        Mobile proxyInstance = (Mobile) Proxy.newProxyInstance(Car.class.getClassLoader(), Car.class.getInterfaces(),  new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                long before = System.currentTimeMillis();
                System.out.println(method.getName()+"方法被执行前。。。");
                Object invoke = method.invoke(car, args);
                System.out.println(method.getName()+"方法被执行后。。。");
                long after = System.currentTimeMillis();
                System.out.println("耗时:" + (after - before));
                return null;
            }
        });

        proxyInstance.move();
    }
}

创建代理类需要调用Proxy.newProxyInstance()方法,其中有三个参数

  1. 第一个参数:一个类加载器,一般使用被代理类的类加载器
  2. 第二个参数:被代理类实现的接口
  3. 第三个参数:一个InvocationHandler接口的实现类

可以把动态代理的创建改成这样:自己写一个类实现InvocationHandler。

public class Test {
    public static void main(String[] args) {
        Car car = new Car();
        System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
        Mobile proxyInstance = (Mobile) Proxy.newProxyInstance(Car.class.getClassLoader(), Car.class.getInterfaces(), new MyInvocationHandler(car));
        proxyInstance.move();
    }

}
class MyInvocationHandler implements InvocationHandler{
    private Car car;

    public MyInvocationHandler(Car car) {
        this.car = car;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long before = System.currentTimeMillis();
        System.out.println(method.getName()+"方法被执行前。。。");
        Object invoke = method.invoke(car, args);
        System.out.println(method.getName()+"方法被执行后。。。");
        long after = System.currentTimeMillis();
        System.out.println("耗时:" + (after - before));
        return null;
    }
}

要想搞懂JDK动态代理必须查看动态生成代理类,通过设置JDK动态代理生成的类是否保存的一个属性将生成的代理类保存下来,通过在程序启动前加上:

 System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles",true);
代理类的调用过程

生成的代理类

public final class $Proxy0 extends Proxy implements Mobile {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void move() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("com.shaoby.basic.proxy.JdkProxy.Mobile").getMethod("move");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}
  1. 可以看到代理类继承了Proxy,并实现了我们定义的Mobile接口
  2. 构造方法传入InvocationHandler类型,并赋值给父类Proxy中的h参数,可以猜测这个就在获取代理类时候的第三个参数;
  3. 代理类中通过反射生成了四个方法,除了Object中的equals、toString、hashCode外另外一个就是我们要代理的方法move;
  4. 在move方法中调用了 super.h.invoke(this, m3, (Object[])null),即MyInvocationHandler中的invoke方法。
  5. 因此在调用代理类的invoke方法时,调用的就是MyInvocationHandler中的invoke方法。
Method类

这个类是一个方法的类,说白了就是方法的模板,在这个类中有一个方法invoke,传入两个参数,一个是调用方法的对象,另一个是方法的入参

InvocationHandler中的invoke方法

可以看到在代理类中invoke方法的调用为 super.h.invoke(this, m3, (Object[])null);
MyInvocationHandler中的invoke方法:

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long before = System.currentTimeMillis();
        System.out.println(method.getName()+"方法被执行前。。。");
        Object invoke = method.invoke(car, args);
        System.out.println(method.getName()+"方法被执行后。。。");
        long after = System.currentTimeMillis();
        System.out.println("耗时:" + (after - before));
        return null;
    }

参数:

  1. 第一个参数:this,即代理类本身
  2. 第二个参数:m3,即move方法
  3. 第三个参数:第二个方法的入参

返回值:

这个返回值其实是被代理方法的返回值,如果没有返回值就返回null;

这里最重要的是method.invoke(car, args),就是用car对象调用move方法。

ASM

在上述分析中,动态代理类的构造方法中参数是我们猜测的。其实这里是用ASM实现的。在生成代理类时调用的Proxy.newInstance()传入了InvocationHandler接口的实现类,Proxy.neInstance()会动态的生成代理类,并产生一个代理对象。这里后续再研究。

ASM是Java字节码操控框架,它能够以二进制形式修改已有类,或动态生成类,ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。

cglib动态代理

public class Test {

    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Plane.class);
        enhancer.setCallback(new MyMethodInterceptor());
        Plane o = (Plane)enhancer.create();
        o.move();


    }
}
class MyMethodInterceptor implements MethodInterceptor{
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        long before = System.currentTimeMillis();
        System.out.println(method.getName()+"方法被执行前。。。");
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println(method.getName()+"方法被执行后。。。");
        long after = System.currentTimeMillis();
        System.out.println("耗时:" + (after - before));
        return result;
    }
}

cglib动态代理的原理是:

  1. 指定代理类的父类
  2. 生成代理类
  3. 调用代理类父类的method