Java JDK Proxy和CGLib动态代理示例讲解

简介


代理模式在Java中有很多应用场景,而代理又分静态代码和动态代理。静态代理是编写、编译或加载时织入代码实现,而动态代理则在运行时实现。简单而言,静态代理是在运行前就已经存在,而动态代理则在运行时才存在的。而常用的动态代理有两种实现:

  • JDK Proxy: JDK Proxy是JDK自带的,不需要引入外部库,通过实现接口进行代理;

  • CGLib: CGLib是引入第三方库,通过ASM技术来实现字节码的生成;通过继承的方式来实现。

现在我们来通过代码分别展示一下两种方式。

JDK Proxy


JDK Proxy是通过实现接口来实现代理的,我们先定义一个接口:

publicinterfaceFlyable {
    String fly(String route);
}

接着有一个实现类:

publicclassBirdimplementsFlyable {
    @Overridepublic String fly(String route) {
        System.out.println("Route: " + route);
        return route;
    }
}

然后我们需要定义一个InvocationHandler来改动方法的逻辑,就是目标被代理后有什么不同:

publicclassFlyableInvocationimplementsInvocationHandler {
    privatefinal Flyable target;

    publicFlyableInvocation(Flyable target) {
        this.target = target;
    }

    @Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
        longstart= System.nanoTime();
        System.out.println(target + ": ===JDK proxy===");
        Objectresult= method.invoke(this.target, args);
        System.out.println(target + ": ===JDK proxy===");
        longend= System.nanoTime();
        System.out.println("Executing time: " + (end - start) + " ns");
        return result;
    }
}

这里我们在方法调用前后加了日志,同时也计算了一下方法的执行时间。

最终在调用的时候如下:

publicclassJDKDynamicProxy {
    publicstaticvoidmain(String[] args) {
        ClassLoaderclassLoader= JDKDynamicProxy.class.getClassLoader();
        Class<?>[] interfaces = Bird.class.getInterfaces();
        Birdbird=newBird();
        Flyableflyable= (Flyable) Proxy.newProxyInstance(classLoader, interfaces, newFlyableInvocation(bird));
        flyable.fly("Go to pkslow.com");
    }
}

通过Proxy.newProxyInstance方法会生成一个代理的实例,执行这个实例的方法,而原有实例bird被代理了。

执行结果如下:

com.pkslow.basic.jdk.Bird@3551a94: ===JDK proxy===
Route: Go to pkslow.com
com.pkslow.basic.jdk.Bird@3551a94: ===JDK proxy===
Executing time: 18195736 ns

查看代理类


我们还可以查看生成的代理类,可以通过添加VM参数:

# JDK 8
-Dsum.misc.ProxyGenerator.saveGeneratedFiles=true# JDK 11
-Djdk.proxy.ProxyGenerator.saveGeneratedFiles=true

当然,也可以在Java代码中设置系统属性来实现。

设置完成,再执行程序,就会生成代理类的.class文件。

CGLib


CGLib是通过继承来实现的,我们先来定义一个类:

publicclassAnimal {
    public String talk(String str) {
        System.out.println("Talking: " + str);
        return str;
    }
}

然后定义一个Interceptor,这个类的作用就是生成代理实例,且定义如何改变目标方法的执行:

publicclassCGLibProxy<T> implementsMethodInterceptor {
    private T target;

    public T getInstance(T target) {
        this.target = target;
        Enhancerenhancer=newEnhancer();
        enhancer.setSuperclass(this.target.getClass());
        enhancer.setCallback(this);
        return (T) enhancer.create();
    }

    @Overridepublic Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy)throws Throwable {

        longstart= System.nanoTime();
        System.out.println(target + ": ===CGLib proxy===");
        Objectresult= methodProxy.invoke(this.target, args);
        System.out.println(target + ": ===CGLib proxy===");
        longend= System.nanoTime();
        System.out.println("Executing time: " + (end - start) + " ns");

        return result;
    }
}

这里同样是在方法前后加了日志,同时记录时长。

调用如下:

publicclassCGLibDynamicProxy {
    publicstaticvoidmain(String[] args) {
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/Users/larry/IdeaProjects/pkslow-samples/java-basic/jdk-cglib-proxy/target/cglib_proxy_classes");
        CGLibProxy<Animal> cgLibProxy = newCGLibProxy<>();
        Animalanimal= cgLibProxy.getInstance(newAnimal());
        animal.talk("Hi, pkslow");
    }
}

这里设置系统属性是为了把生成的代理类输出到.class文件中,方便学习查看。

执行结果如下:

com.pkslow.basic.cglib.Animal@57855c9a: ===CGLib proxy===
Talking: Hi, pkslow
com.pkslow.basic.cglib.Animal@57855c9a: ===CGLib proxy===
Executing time: 28396871 ns

总结


JDK Proxy本质上使用的是反射的机制,而CGLib使用的是ASM,CGLib速度会更好。但它们都不支持final的类和方法,因为通过接口和继承都无法改变final方法。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

盈梓的博客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值