文章目录
JDK 动态代理 和 CGLib 动态代理
-
动态代理就是:
在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术
。 -
在生成代理对象的过程中,
目标对象不变,代理对象中的方法是目标对象方法的增强方法
。 -
可以理解为
运行期间,对象中方法的动态拦截,在拦截方法的前后执行功能操作
。 -
特点:
方法修改基本无影响:方法增强与原先的功能分开,与方法名称无关
、
动态代理工厂的数量小于静态代理类数量
、代理工厂创建的代理类一般不保存,不占用内存
。
JDK 动态代理
-
JDK 动态代理是:
代理类实例在程序运行时,由 JVM 根据反射机制动态的生成,而不是自行在程序编码期间创建
。 -
创建的是:
代理类对象
。是在运行后才能创建的。 -
代理类是用来:
替待 A 类调用 C 类的
。-
由于某种限制,A 类无法调用 C 类的方法,可以创建一个代理类 B 类。
-
让 B 类访问 C 类,A 类访问 B 类,完成该功能。
-
-
JDK 动态代理,必须有接口,目标类必须实现接口
,且只能代理实现接口的类
。 -
JDK Proxy 是
Java 语言
自带的功能,无需通过加载第三方类实现。 -
JDK Proxy 是
通过拦截器加反射的方式实现
的。
创建代理对象
-
使用
JDK
官方的Proxy
类创建代理对象。 -
Proxy.newProxyInstance(ClassLoader, Class<?>[], InvocationHandler);
-
ClassLoader:
JVM 提供的类加载器
,用来加载类。 -
Class<?>[]:
目标类所实现的接口集
。 -
InvocationHandler:
调用处理程序
,用来调用、增强目标类方法。
核心类
InvocationHandler 接口
-
代理实例的调用处理器需要实现 InvocationHandler 接口
,并且每个代理实例都有一个关联的调用处理器
。 -
创建的
每一个代理实例都要有一个关联的 InvocationHandler
。 -
在
调用代理实例的方法
时,会被转到 InvocationHandler 的 invoke 方法
上。 -
只有一个 invoke() 方法,在方法内调用目标类的方法(所有方法都可以调用),同时在调用前后可以
增强方法功能
。 -
invoke() 方法的三个参数:
-
proxy(Object):是
调用该方法的代理实例
。 -
method(Method):是在
代理实例上调用的接口方法对应的 Method 实例
。 -
args(Object[]):
一个 Object 数组,是在代理实例上的方法调用中传递的参数值;如果接口方法为无参,则该值为 null
。 -
返回值:
调用代理实例上的方法的返回值
。
-
Proxy 类
-
提供了创建动态代理类及其实例的静态方法
,该类也是动态代理类的超类
。 -
代理类的
名称以 $Proxy 开头,后面跟着一个数字序号
,如 P r o x y 0 、 Proxy0、 Proxy0、Proxy1。 -
代理类
继承了 Proxy 类,类实现了创建时指定的接口(JDK动态代理是面向接口的)
。 -
每个代理类都
有一个公共构造函数
,它接受一个参数(接口 InvocationHandler 的实现),用于设置代理实例的调用处理器
。
示例代码
-
代理目标接口 UsbSellProxy 中,定义了两个方法,代理目标接口的实现类 UsbSellProxyImpl 实现了这两个方法。
-
JDK 的代理工厂用来创建 UsbSellProxy 接口的代理类。
-
在方法调用上,没有区别。
-
方法增强是对所有方法的,所有方法都要经过 InvocationHandler 的 invoke() 方法调用
。
public class ProxyTest {
/**
* 动态代理
* <p>
* JDK
*/
public static void main(String[] args) {
// 目标对象
UsbSellProxyImpl usbSellProxy = new UsbSellProxyImpl();
// 使用 JDK 动态代理为【目标对象】创建代理对象
UsbSellProxyImpl proxy = JdkProxyFactory.getProxy(usbSellProxy);
System.out.println(proxy.sell(3));
System.out.println(proxy.batchSell(30));
}
}
/**
* 代理目标 类(必须要有)
* <p>
* 动态 代理 测试
* <p>
* 或者说是 A、B、C 三个类都要实现的公共方法接口
* <p>
* 表示功能的接口,厂家,商家都要完成的功能
*/
interface UsbSellProxy {
/**
* 购买 USB
*
* @param amount 购买数量
*
* @return 总价
*/
float sell(int amount);
/**
* 批量 购买 USB
* <p>
* 打折
*
* @param amount 购买数量
*
* @return 打折后的总价
*/
float batchSell(int amount);
}
/**
* 动态 代理 测试
* <p>
* 代理目标 类 的实现类
*/
class UsbSellProxyImpl implements UsbSellProxy {
/**
* USB 单价
*/
private float price = 128F;
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
@Override
public float sell(int amount) {
System.out.println("动态代理 接口实现类(单买)");
System.out.println("USB 单价:" + price);
if(amount <= 0) {
System.out.println("购买 USB 的数量不对,结束!");
return 0;
}
System.out.println("发出 USB " + amount + " 件。");
return amount * price;
}
@Override
public float batchSell(int amount) {
System.out.println("动态代理 接口实现类(批发)");
System.out.println("USB 单价:" + price);
if(amount <= 0) {
System.out.println("购买 USB 的数量不对,结束!");
return 0;
}
System.out.println("发出 USB " + amount + " 件。");
float result = amount * price;
if(amount <= 10) {
System.out.println("批量购买,打【九五】折!");
result = 0.95F * result;
} else if(amount <= 20) {
System.out.println("批量购买,打【九】折!");
result = 0.9F * result;
} else if(amount <= 30) {
System.out.println("批量购买,打【八五】折!");
result = 0.85F * result;
} else {
System.out.println("批量购买,打【八】折!");
result = 0.8F * result;
}
return result;
}
}
/**
* JDK 动态代理
* <p>
* 生成代理类的工厂
*/
class JdkProxyFactory {
/**
* 创建 target 类的代理对象
* <p>
* 当调用代理对象中的方法时,其实就是调用的 InvocationHandler里面的 invoke 方法,
* 然后在 invoke 方法里调用目标对象对应的方法
* <p>
* 即:Java 反射中调用类方法
*
* @param target 要创建代理对象的类
* @param <T> 泛型
*
* @return 代理对象
*/
public static <T> T getProxy(final Object target) {
// 创建代理实例
// Proxy 的静态方法 newProxyInstance() 参数分别是:target 类的类加载器、target 类实现的接口、InvocationHandler
Object proxyInstance = Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
(proxy, method, args) -> {
System.out.println("执行目标类的目标方法前!(可以适当增强方法)");
Object invoke = method.invoke(target, args);
System.out.println("执行目标类的目标方法后!(可以适当增强方法)");
return invoke;
}
);
// 返回代理对象
return (T) proxyInstance;
}
}
CGLib(Code Generation Library) 动态代理
-
CGLIB 通过
动态生成一个需要被代理类的子类(即被代理类作为父类)
,该子类重写被代理类的所有不是 final 修饰的方法
,并在子类中采用方法拦截的技术拦截父类所有的方法调用
,进而织入横切逻辑。 -
因为
CGLIB 采用整型变量建立了方法索引,这比使用 JDK 动态代理(使用 Java 反射技术创建代理类的实例)更快
。 -
第三方的 CGLib,如果出现 asmXXXX 异常,就需要导入 asm.jar 包。
-
而
没有接口时,需要使用 CGLib 动态代理
。 -
被代理对象不能用 final 修饰。
-
CGLib 无需通过接口来实现,它是针对类实现代理
,主要是对指定的类生成一个子类,它是通过实现子类的方式来完成调用的
。 -
类包:
org.springframework.cglib.proxy
。
创建代理对象
-
使用
CGLIB
的Enhancer
类创建代理对象。 -
Enhancer.create(Class, MethodInterceptor);
-
Class:
目标类的 Class 对象
。 -
MethodInterceptor:
方法拦截器
,用来调用、增强目标类方法。- 同样的,在 invoke() 方法中调用目标类的方法(所有方法都可以调用),同时在调用前后可以
增强方法功能
。
- 同样的,在 invoke() 方法中调用目标类的方法(所有方法都可以调用),同时在调用前后可以
核心类
Enhancer 类
-
Enhancer 是一个
类的增强器,可以完成对类的代理
。 -
在 Spring 中经常可以看到他的身影,比如
@Configuration 注解的类就会被 Enhancer 代理
。 -
Enhancer 目的就是:
完成代理
,所以我们称之为代理类
,而被代理的类我们称目标类
。
MethodInterceptor 接口
-
是一个拦截器,父类(代理目标类)在调用方法时,都会被其拦截,跳转到 intercept() 方法。
-
Callback:
声明式接口
,主要利用其子接口自定义实现类完成代理对象的逻辑增强
,作用相当于 JDK 动态代理中的 InvocationHandler
。 -
MethodInterceptor 就是 Callback 的子接口之一
,或者说:Enhancer.create() 方法的第二个参数是 Callback 接口
。 -
intercept() 方法有四个参数:
-
o(Object):是
调用该方法的代理实例
。 -
method(Method):是在
代理实例上调用的接口方法对应的 Method 实例
。 -
objects(Object[]):
一个 Object 数组,是在代理实例上的方法调用中传递的参数值;如果接口方法为无参,则该值为 null
。 -
methodProxy(MethodProxy):是在
代理实例上调用的接口方法对应的 Method 实例的代理对象
。 -
返回值:
调用代理实例上的方法的返回值
。
-
示例代码
-
代理目标接口 UsbSellProxy 中,定义了两个方法,代理目标接口的实现类 UsbSellProxyImpl 实现了这两个方法。
-
CGLib 的代理工厂用来创建 UsbSellProxyImpl 类的代理类。
-
在方法调用上,没有区别。
-
方法增强是对所有方法的,所有方法都要经过 MethodInterceptor 的 intercept() 方法调用
。
public class ProxyTest {
/**
* 动态代理
* <p>
* CgLib
*/
public static void main(String[] args) {
// 目标对象
UsbSellProxyImpl usbSellProxy = new UsbSellProxyImpl();
// 使用 JDK 动态代理为【目标对象】创建代理对象
UsbSellProxyImpl proxy = CglibProxyFactory.getProxy(usbSellProxy);
System.out.println(proxy.sell(3));
System.out.println(proxy.batchSell(30));
}
}
/**
* 代理目标 类(必须要有)
* <p>
* 动态 代理 测试
* <p>
* 或者说是 A、B、C 三个类都要实现的公共方法接口
* <p>
* 表示功能的接口,厂家,商家都要完成的功能
*/
interface UsbSellProxy {
/**
* 购买 USB
*
* @param amount 购买数量
*
* @return 总价
*/
float sell(int amount);
/**
* 批量 购买 USB
* <p>
* 打折
*
* @param amount 购买数量
*
* @return 打折后的总价
*/
float batchSell(int amount);
}
/**
* 动态 代理 测试
* <p>
* 代理目标 类 的实现类
*/
class UsbSellProxyImpl implements UsbSellProxy {
/**
* USB 单价
*/
private float price = 128F;
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
@Override
public float sell(int amount) {
System.out.println("动态代理 接口实现类(单买)");
System.out.println("USB 单价:" + price);
if(amount <= 0) {
System.out.println("购买 USB 的数量不对,结束!");
return 0;
}
System.out.println("发出 USB " + amount + " 件。");
return amount * price;
}
@Override
public float batchSell(int amount) {
System.out.println("动态代理 接口实现类(批发)");
System.out.println("USB 单价:" + price);
if(amount <= 0) {
System.out.println("购买 USB 的数量不对,结束!");
return 0;
}
System.out.println("发出 USB " + amount + " 件。");
float result = amount * price;
if(amount <= 10) {
System.out.println("批量购买,打【九五】折!");
result = 0.95F * result;
} else if(amount <= 20) {
System.out.println("批量购买,打【九】折!");
result = 0.9F * result;
} else if(amount <= 30) {
System.out.println("批量购买,打【八五】折!");
result = 0.85F * result;
} else {
System.out.println("批量购买,打【八】折!");
result = 0.8F * result;
}
return result;
}
}
/**
* cglib 动态代理
* <p>
* 生成代理类的工厂
*/
class CglibProxyFactory {
/**
* 创建 target 类的代理对象
*
* @param target 要创建代理对象的类
* @param <T> 泛型
*
* @return 代理对象
*/
public static <T> T getProxy(final Object target) {
// 创建代理实例
// Enhancer 的静态方法 create() 参数分别是:target 类的 Class 对象、MethodInterceptor
Object proxyInstance = Enhancer.create(
target.getClass(),
(MethodInterceptor) (o, method, objects, methodProxy) -> {
System.out.println("执行目标类的目标方法前!(可以适当增强方法)");
Object invoke = method.invoke(target, objects);
System.out.println("执行目标类的目标方法后!(可以适当增强方法)");
return invoke;
});
// 返回代理对象
return (T) proxyInstance;
}
}
JDK 动态代理 和 CGLib 动态代理 的区别
-
JDK 动态代理
只能对接口进行代理
,不能对普通的类进行代理,这是因为 JDK 动态代理生成的代理类,其父类是 Proxy,且 Java 不支持类的多继承
。 -
CGLib 动态代理
能够代理接口和普通的类
,但是被代理的类不能被 final 修饰
,且接口中的方法不能使用 final 修饰
。 -
JDK 动态代理
使用 Java 反射技术进行操作,在生成类上更高效
。 -
CGLib 动态代理
使用 ASM 框架直接对字节码进行修改,使用了 FastClass 的特性
。在某些情况下,类的方法执行会比较高效。 -
FastClass 机制就是
对一个类的方法建立索引,通过索引来直接调用相应的方法
。 -
CGLib 动态代理 是第三方提供的工具,
基于 ASM 实现的,性能比较高
。 -
Java 对 JDK 动态代理 提供了稳定的支持,并且
会持续的升级和更新
,Java 8 版本中的 JDK Proxy 性能相比于之前版本提升了很多。
动态代理的作用
-
功能增强
:在不改变原来目标方法功能的前提下,可以在代理中增强自己的功能代码。 -
控制访问
:代理类规定好访问目标,不能越界。
静态代理
-
代理类是自己手工实现的,自己创建一个 java类,表示代理类
。(在编码期创建代理类)
-
所要代理的目标类是确定的
。 -
特点:
实现简单
、容易理解
。 -
缺点:
代理类数量过多:当目标类增加了,代理类可能也需要成倍的增加
、
修改时影响大:接口中功能增加了、或者修改了,会影响众多的实现类、厂家类、代理类,都需要修改
。
/**
* 静态 代理 测试
* <p>
* 表示功能的接口,厂家,商家都要完成的功能
*/
interface UsbSell {
/**
* 购买 USB
*
* @param amount 购买数量
*
* @return 总价
*/
float sell(int amount);
}
public class JdkProxyTest {
/**
* USB 买家
*/
public static void main(String[] args) {
// 创建商家 1
UsbBusinessOne business = new UsbBusinessOne();
int amount = 5;
// 购买 USB
float price = business.sell(amount);
System.out.println("买家通过商家 " + business.getClass().toString() + " 购买 USB " + amount + " 件,总价:" + price);
System.out.println("========================");
// 创建商家 2
UsbBusinessTwo business2 = new UsbBusinessTwo();
// 购买 USB
float price1 = business2.sell(amount);
System.out.println("买家通过商家 " + business2.getClass().toString() + " 购买 USB " + amount + " 件,总价:" + price1);
}
}
/**
* USB 生产工厂 1(金士顿)
*/
class UsbFactoryOne implements UsbSell {
/**
* USB 单价
*/
private float price = 128F;
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
@Override
public float sell(int amount) {
System.out.println("USB 生产工厂 UsbFactoryOne(金士顿),Usb 单价:" + price);
if(amount <= 0) {
return 0;
}
System.out.println("USB 生产工厂 UsbFactoryOne(金士顿)发出 USB " + amount + " 件。");
return amount * price;
}
}
/**
* USB 生产工厂 2(华为)
*/
class UsbFactoryTwo implements UsbSell {
/**
* USB 单价
*/
private float price = 106F;
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
@Override
public float sell(int amount) {
System.out.println("USB 生产工厂 UsbFactoryTwo(华为),Usb 单价:" + price);
if(amount <= 0) {
return 0;
}
System.out.println("USB 生产工厂 UsbFactoryTwo(华为)发出 USB " + amount + " 件。");
return amount * price;
}
}
/**
* USB 商家 1(金士顿 旗舰店)
*/
class UsbBusinessOne implements UsbSell {
/**
* 声明商家代言的工厂
*/
private UsbFactoryOne factory = new UsbFactoryOne();
/**
* 单件溢价
*/
private float premium = 20F;
public float getPremium() {
return premium;
}
public void setPremium(float premium) {
this.premium = premium;
}
@Override
public float sell(int amount) {
System.out.println("USB 商家 UsbBusinessOne(金士顿 旗舰店),USB 单件溢价:" + premium);
if(amount <= 0) {
return 0;
}
// 成本
float cost = factory.sell(amount);
System.out.println("USB 商家 UsbBusinessOne(金士顿 旗舰店),购入 USB " + amount + " 件,并发出。");
return cost + premium * amount;
}
}
/**
* USB 商家 2(华为 旗舰店)
*/
class UsbBusinessTwo implements UsbSell {
/**
* 声明商家代言的工厂
*/
private UsbFactoryTwo factory = new UsbFactoryTwo();
/**
* 单件溢价
*/
private float premium = 22F;
public float getPremium() {
return premium;
}
public void setPremium(float premium) {
this.premium = premium;
}
@Override
public float sell(int amount) {
System.out.println("USB 商家 UsbBusinessTwo(华为 旗舰店),USB 单件溢价:" + premium);
if(amount <= 0) {
return 0;
}
// 成本
float cost = factory.sell(amount);
System.out.println("USB 商家 UsbBusinessTwo(华为 旗舰店),购入 USB " + amount + " 件,并发出。");
return cost + premium * amount;
}
}
-
从代码中看出,明确好目标类后,使用上十分方便,相应的类的数量也会变多。
-
同样的,方法的增强要重复添加,不同名称功能相近的方法也需要重复添加。