Java JDK Proxy和CGLib动态代理示例讲解
简介
代理模式在Java中有很多应用场景,而代理又分静态代码和动态代理。静态代理是编写、编译或加载时织入代码实现,而动态代理则在运行时实现。简单而言,静态代理是在运行前就已经存在,而动态代理则在运行时才存在的。而常用的动态代理有两种实现:
- JDK Proxy: JDK Proxy是JDK自带的,不需要引入外部库,通过实现接口进行代理;
 - CGLib: CGLib是引入第三方库,通过ASM技术来实现字节码的生成;通过继承的方式来实现。
 
现在我们来通过代码分别展示一下两种方式。
JDK Proxy
JDK Proxy是通过实现接口来实现代理的,我们先定义一个接口:
public interface Flyable {
    String fly(String route);
}
接着有一个实现类:
public class Bird implements Flyable {
    @Override
    public String fly(String route) {
        System.out.println("Route: " + route);
        return route;
    }
}
然后我们需要定义一个InvocationHandler来改动方法的逻辑,就是目标被代理后有什么不同:
public class FlyableInvocation  implements InvocationHandler {
    private final Flyable target;
    public FlyableInvocation(Flyable target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long start = System.nanoTime();
        System.out.println(target + ": ===JDK proxy===");
        Object result = method.invoke(this.target, args);
        System.out.println(target + ": ===JDK proxy===");
        long end = System.nanoTime();
        System.out.println("Executing time: " + (end - start) + " ns");
        return result;
    }
}
这里我们在方法调用前后加了日志,同时也计算了一下方法的执行时间。
最终在调用的时候如下:
public class JDKDynamicProxy {
    public static void main(String[] args) {
        ClassLoader classLoader = JDKDynamicProxy.class.getClassLoader();
        Class<?>[] interfaces = Bird.class.getInterfaces();
        Bird bird = new Bird();
        Flyable flyable = (Flyable) Proxy.newProxyInstance(classLoader, interfaces, new FlyableInvocation(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是通过继承来实现的,我们先来定义一个类:
public class Animal {
    public String talk(String str) {
        System.out.println("Talking: " + str);
        return str;
    }
}
然后定义一个Interceptor,这个类的作用就是生成代理实例,且定义如何改变目标方法的执行:
public class CGLibProxy<T> implements MethodInterceptor {
    private T target;
    public T getInstance(T target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());
        enhancer.setCallback(this);
        return (T) enhancer.create();
    }
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        long start = System.nanoTime();
        System.out.println(target + ": ===CGLib proxy===");
        Object result = methodProxy.invoke(this.target, args);
        System.out.println(target + ": ===CGLib proxy===");
        long end = System.nanoTime();
        System.out.println("Executing time: " + (end - start) + " ns");
        return result;
    }
}
这里同样是在方法前后加了日志,同时记录时长。
调用如下:
public class CGLibDynamicProxy {
    public static void main(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 = new CGLibProxy<>();
        Animal animal = cgLibProxy.getInstance(new Animal());
        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方法。
代码请看GitHub: https://github.com/LarryDpk/pkslow-samples
Java JDK Proxy和CGLib动态代理示例讲解的更多相关文章
- Java Proxy和CGLIB动态代理原理
		
动态代理在Java中有着广泛的应用,比如Spring AOP,Hibernate数据查询.测试框架的后端mock.RPC,Java注解对象获取等.静态代理的代理关系在编译时就确定了,而动态代理的代理关 ...
 - cglib代理与jdk动态代理示例
		
先看基于jdk实现的动态代理实现例子 1.先声明一个接口类 public interface UserService{ public String getName(String msg); } 2.实 ...
 - 【Java EE 学习 51】【Spring学习第三天】【cglib动态代理】【AOP和动态代理】【切入点表达式】
		
一.cglib动态代理 1.简介 (1)CGlib是一个强大的,高性能,高质量的Code生成类库.它可以在运行期扩展Java类与实现Java接口. (2) 用CGlib生成代理类是目标类的子类. (3 ...
 - Spring源码剖析5:JDK和cglib动态代理原理详解
		
AOP的基础是Java动态代理,了解和使用两种动态代理能让我们更好地理解 AOP,在讲解AOP之前,让我们先来看看Java动态代理的使用方式以及底层实现原理. 转自https://www.jiansh ...
 - AOP的底层实现:JDK动态代理与Cglib动态代理
		
转载自 https://www.cnblogs.com/ltfxy/p/9872870.html SpringAOP底层的实现原理: JDK动态代理:只能对实现了接口的类产生代理.(实现接口默认JDK ...
 - JDK动态代理和CGLIB动态代理编码
		
JDK动态代理[接口]: import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import jav ...
 - 十 Spring的AOP的底层实现:JDK动态代理和Cglib动态代理
		
SpringAOP底层的实现原理: JDK动态代理:只能对实现了接口的类产生代理.(实现接口默认JDK动态代理,底层自动切换) Cglib动态代理(类似Javassist第三方的代理技术):对没有实现 ...
 - cglib源码分析(四):cglib 动态代理原理分析
		
本文分下面三个部分来分析cglib动态代理的原理. cglib 动态代理示例 代理类分析 Fastclass 机制分析 一.cglib 动态代理示例 public class Target{ publ ...
 - Java提高班(六)反射和动态代理(JDK Proxy和Cglib)
		
反射和动态代理放有一定的相关性,但单纯的说动态代理是由反射机制实现的,其实是不够全面不准确的,动态代理是一种功能行为,而它的实现方法有很多.要怎么理解以上这句话,请看下文. 一.反射 反射机制是 Ja ...
 - 动态代理:JDK原生动态代理(Java Proxy)和CGLIB动态代理原理+附静态态代理
		
本文只是对原文的梳理总结,以及自行理解.自己总结的比较简单,而且不深入,不如直接看原文.不过自己梳理一遍更有助于理解. 详细可参考原文:http://www.cnblogs.com/Carpenter ...
 
随机推荐
- Python基础之函数:5、内置函数、迭代器对象、异常的捕获和处理
			
目录 一.重要内置函数 1.zip() 2.filter() 3.sorted() 二.常见内置函数 1. abs() 2.all.any() 3.bin.oct.hex.int() 4.bytes( ...
 - Python基础部分:9、数据的类型和内置方法
			
目录 一.数据类型内置方法理论 1.什么是数据内置方法 2.如何调用数据内置方法 二.整型(int)内置方法与操作 1.类型转换 2.进制数转换 三.浮点型(float)内置方法与操作 1.类型转换 ...
 - 46.drf过滤、搜索、排序
			
DRF的过滤类 drf过滤器在filters模块中,主要有四个类 BaseFilterBackend:过滤基类,留好占位方法待后续继承 SearchFilter:继承BaseFilterBackend ...
 - Halcon使用MeasurePos来实现检测边缘点
			
(1)为了提高性能,测量句柄只需要初始化一次: 参数:测量矩形的中心点行坐标,测量矩形中心的列坐标,测量矩形的角度,测量矩形的宽,测量矩形的高,待处理图像的宽,待处理图像的高,使用的算法,输出测量句柄 ...
 - 第2-3-5章 删除附件的接口开发-文件存储服务系统-nginx/fastDFS/minio/阿里云oss/七牛云oss
			
目录 5.4 接口开发-根据id删除附件 5.4.1 接口文档 5.4.2 代码实现 5.4.3 接口测试 5.4.4 测试ALI和FAST_DFS以及MINIO上传和删除的接口 5.4.4.1 阿里 ...
 - ROS应用层通信协议解析
			
参考:http://wiki.ros.org/ROS/Master_API http://wiki.ros.org/ROS/Connection Header 说明 ROS本质上就是一个松耦合的通信框 ...
 - 使用Python实现多线程、多进程、异步IO的socket通信
			
多线程实现socket通信服务器端代码 import socket import threading class MyServer(object): def __init__(self): # 初始化 ...
 - Zabbix技术分享——使用Zabbix6.0监控业务日志
			
企业日常IT运维过程中,常会碰到需要监控业务日志的情况,以下将介绍如何使用Zabbix6.0监控业务日志. 应用场景描述: 企业IT运维部门使用自建zabbix平台对公司某业务系统进行了监控.近段时间 ...
 - 【Java】FileUtils-获取路径的所有文件(或文件夹)
			
一.获取指定路径下的所有Excel文件 package com.boulderaitech.utils; import java.io.File; import java.util.Arrays; p ...
 - 【每日一题】【哈希表,返回结果的下标】2022年1月18日-NC61 两数之和
			
描述给出一个整型数组 numbers 和一个目标值 target,请在数组中找出两个加起来等于目标值的数的下标,返回的下标按升序排列.(注:返回的数组下标从1开始算起) 算法: import java ...