JDK 动态代理 - 使用方法

JDK动态代理使用方法

JDK 动态代理针对接口,只有实现了接口的类能使用JDK动态代理

代码实例

public class JdkProxyDemo {

    interface Foo {
        void foo();
    }

    static class Target implements Foo {
        public void foo() {
            System.out.println("target foo");
        }
    }

    public static void main(String[] param) {
        // 目标对象
        Target target = new Target();
        // 代理对象
        Foo proxy = (Foo) Proxy.newProxyInstance(
                Target.class.getClassLoader(), new Class[]{Foo.class},
                (p, method, args) -> {
                    System.out.println("proxy before...");
                    Object result = method.invoke(target, args);
                    System.out.println("proxy after...");
                    return result;
                });
        // 调用代理
        proxy.foo();
    }
}

上面代码实例中,有个静态内部类Target,有个接口Foo,Target类实现了Foo接口
现在要对Target类中的Foo接口方法foo,进行增强
调用官方提供的API,Proxy.newProxyInstance,传入相应参数后获得一个代理对象

分析参入的参数

传入的参数总共有三个

第一个参数 要求我们传入一个类加载器,因为代理类是没有源码的,而是在运行期间直接生成代理类的字节码,生成的字节码需要被加载后才能运行,因此需要传入一个类加载器来做加载操作,可以直接传入被代理类的类加载器

第二个参数 要求传入一个接口数组,用于指定需要增强的方法是哪些。一个类可以实现很多个接口,传入了哪些接口,那么被代理类中的这些接口的方法都会被增强,如果不是这些接口的方法,就不会被增强

第三个参数 用于定义具体的增强方式,传入 InvocationHandler 接口的实现类,因为 InvocationHandler 是函数式接口,因此上面的代码使用了Lambda表达式进行实现

这是 InvocationHandler 接口中唯一的方法 invoke ,也是我们需要实现的方法

public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;

方法中有三个参数

  • proxy是代理对象自己
  • method是当前要增强的方法
  • args是方法需要的参数

在增强方法中进行了 System.out.println("proxy before...");System.out.println("proxy after..."); 增强

而原本的逻辑我们使用反射进行 method.invoke(target, args)

JDK 动态代理原理

如果我们直接去看 Proxy.newProxyInstance 方法的源码,会很难去看懂内部原理,因为其内部是通过ISM动态生成代理对象的字节码的,但是我们可以通过反编译查看生成的代理对象的源码,如下:

生成动态代理的代码如下:

public class JdkProxyDemo {

    interface Foo {
        void foo();
        int bar();
    }

    static class Target implements Foo {
        public void foo() {
            System.out.println("target foo");
        }
        public int bar(){
			System.out.println("target bar");
			return 100;
		}
    }

    public static void main(String[] param) {
        // 目标对象
        Target target = new Target();
        // 代理对象
        Foo proxy = (Foo) Proxy.newProxyInstance(
                Target.class.getClassLoader(), new Class[]{Foo.class},
                (p, method, args) -> {
                    System.out.println("proxy before...");
                    Object result = method.invoke(target, args);
                    System.out.println("proxy after...");
                    return result;
                });
        // 调用代理
        proxy.foo();
    }
}

以下是我根据反编译代码仿写出的代理对象源码,真正的代理对象更复杂,而且还重写了 equals 、 hashCode 和 toString方法:

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

public class $Proxy0 extends Proxy implements JdkProxyDemo.Foo {

    public $Proxy0(InvocationHandler h) {
        super(h);
    }
    
    @Override
    public void foo() {
        try {
            h.invoke(this, foo, new Object[0]);
        } catch (RuntimeException | Error e) {
            throw e;
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }

    @Override
    public int bar() {
        try {
            Object result = h.invoke(this, bar, new Object[0]);
            return (int) result;
        } catch (RuntimeException | Error e) {
            throw e;
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }

    static Method foo;
    static Method bar;
    static {
        try {
            foo = JdkProxyDemo.Foo.class.getMethod("foo");
            bar = JdkProxyDemo.Foo.class.getMethod("bar");
        } catch (NoSuchMethodException e) {
            throw new NoSuchMethodError(e.getMessage());
        }
    }
}

代理对象继承了 Proxy 类,在 Proxy 类中有 InvocationHandler 属性
代理对象实现了Foo接口,并重写 foo 和 bar 方法,但是重写的方式都一样,直接回调了 InvocationHandler 属性的 invoke
代理对象还会获取 Foo 接口中方法的方法对象

代理对象中的 InvocationHandler 属性 是什么?
我们在调用 Proxy.newProxyInstance 方法时,已经传入了我们自定义实现的 InvocationHandler 对象,并且 Proxy.newProxyInstance 在构造 $Proxy0 对象时,就把我们传入的 InvocationHandler 对象,传给 public $Proxy0(InvocationHandler h) 构造方法,因此 $Proxy0 中的 InvocationHandler 属性就是我们自定义实现的 InvocationHandler 对象

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值