定义:为其他对象提供一种代理以控制对这个对象的访问
即通过代理对象访问目标对象,这样做的好处是,可以在目标对象实现的基础上,增强额外的功能操作,即拓展目标对象的功能。
解决的问题
在不修改源代码的情况下,对目标对象进行增强。
优势就是可以在运行时进行增强(动态代理)
核心要点:
代理是为了扩展类而存在的,可以控制对目标类的服务的访问。
增加中间层,多了一层控制,多了一层拓展。
代理模式有不同的表现形式,静态代理、动态代理(JDK的动态代理、Cglib的动态代理)
类图:
代码实现
需求:通过汽车代理买车的利息
静态代理
静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者继承父类
优点:在不修改目标对象的前提下,能通过代理对象对目标功能拓展
缺点:由于代理对象需要与目标对象是实现一样的接口,所以会与很多代理类
一旦接口增加方法,目标对象与代理对象都需要维护
买车接口BuyCar
/**
* 买车的接口
*
* @author Promsing(张有博)
* @version 1.0.0
* @since 2022/8/18 - 17:56
*/
public interface BuyCar {
public void buyCar();
}
真实类-目标对象
/**
* @Author: Promsing
* @Date: 2021/4/3 - 8:25
* @Description: 实现类
* @version: 1.0
*/
public class BuyCarImpl implements BuyCar {
@Override
public void buyCar() {
System.out.println("我要买车~~~啦啦啦,真实对象");
}
}
代理类
/**
* @Author: Promsing
* @Date: 2021/4/3 - 8:26
* @Description: 代理类
* @version: 1.0
*/
public class BuyCarProxy implements BuyCar {
//静态代理- 的实现方式
private BuyCar buyCar;
//注意final修饰的关键字 不可修改
//构造函数注入,需要被代理的对象
public BuyCarProxy(final BuyCar buyCar) {
this.buyCar = buyCar;
}
public void buyCar() {
System.out.println("代理类!买车前的准备~~~");
buyCar.buyCar();
System.out.println("代理类!买完车了,出去浪~~~");
}
}
Main方法测试
/**
* 静态代理测试类
*
* @author Promsing(张有博)
* @version 1.0.0
* @since 2022/9/7 - 15:19
*/
public class Main {
public static void main(String[] args) {
//创建代理对象,并指定目标对象
BuyCar buyCar=new BuyCarProxy(new BuyCarImpl());
//通过代理对象,对buyCar进行增强
buyCar.buyCar();
}
}
动态代理
JDK动态代理
代理类所在包:java.lang.reflect.Proxy
JDK动态代理是通过JDK自带的Proxy类中的newProxyInstance()方法来动态生成代理对象的。
我们需要实现InvocationHandler接口,在其invoke()方法中编写调用目标对象的代码。
JDK 动态代理类使用步骤:
- 定义一个接口及其实现类;
- 实现 InvocationHandler接口 并重写invoke方法,在 invoke 方法中我们会调用原生方法(被代理类的方法)并自定义一些处理逻辑;
- 通过 Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法创建代理对象;
动态代理类
/**
* @Author: Promsing
* @Date: 2022/4/3 - 9:09
* @Description: 动态代理类
* @version: 1.0
*/
public class DynamicProxy implements InvocationHandler {
//想相当于切点
//被代理的真实对象
private Object object;
public DynamicProxy( Object object) {
this.object = object;
}
/**
*
* @param proxy 目标对象的引用
* @param method 当前执行的方法
* @param args 当前执行方法所需的参数
* @return 和被代理对象方法有相同的返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(this.toString());
System.out.println("不贷款,全款!买车前的准备~~~"); //主要增强
Object result = method.invoke(object, args);
System.out.println("买完车了,出去浪~~~"); //主要增强
return result;
}
}
Main方法测试
/**
* 基于接口的动态代理
*
* @author Promsing(张有博)
* @version 1.0.0
* @since 2022/8/18 - 20:06
*/
public class Main {
public static void main(String[] args) {
System.out.println("-+-+-+使用基于接口的代理-+-+-+");
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
System.out.println("$Proxy0.class全名: "+Proxy.getProxyClass(BuyCar.class.getClassLoader(), BuyCar.class).toString());
//使用DynamicProxy类
//声明一个final修饰的对象--目标类
final BuyCarImpl car = new BuyCarImpl();
Object o = Proxy.newProxyInstance(
car.getClass().getClassLoader(),
car.getClass().getInterfaces(),
new DynamicProxy(car));//相对于生成class文件,并加载到内存,并且获取实例
BuyCar proxy2 = (BuyCar) o; //强转,这里必须使用接口
proxy2.buyCar(); //执行代理类的方法
}
}
运行时代理对象的类型
动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以不存在代理类的字节码文件。代理类和真实类类的关系是在程序运行时确定。 动态代理根据接口或目标对象,计算出代理类的字节码,然后再加载到JVM中使用。
其实现原理如下:由于JVM通过字节码的二进制信息加载类的,那么,如果我们在运行期系统中,遵循Java编译系统组织.class文件的格式和结构,生成相应的二进制数据,然后再把这个二进制数据加载转换成对应的类, 这样,就完成了在代码中,动态创建一个类的能力了。
Cglib动态代理
由于JDK动态代理是面向接口的,也就是说如果目标类没有相应的接口,JDK动态代理就无法为其创建代理。这时可以选择用CGLIB动态代理来实现。
Cglib代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象拓展。
Cglib包的底层通过字节码处理框架ASM来转换字节码并生成新的类
需要引入Jar包–cglib 本案例使用cglib3.3.0
CGLIB 动态代理类使用步骤
- 实现 MethodInterceptor接口 并重写 intercept 方法,intercept 用于拦截增强被代理类的方法,和 JDK 动态代理中的 invoke 方法类似;
- 通过 Enhancer 类的 create()创建代理类
- 客户端使用代理对象
动态代理类
/**
* 实现方法拦截的接口
*
* @author Promsing(张有博)
* @version 1.0.0
* @since 2022/8/19 - 0:00
*/
public class DynamicSubClass implements MethodInterceptor {
//真实对象
private Object target;
public DynamicSubClass(Object target) {
this.target = target;
}
//返回一个代理对象
public Object getProxyInstance(){
//创建工具类
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(target.getClass());
//回调函数
enhancer.setCallback(this);
//创建代理对象
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("不贷款,全款!买车前的准备~~~"); //增强的逻辑
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("买完车了,出去浪~~~");
return result;
}
}
Main方法测试
public class Main2 {
public static void main(String[] args) {
//使用基于子类的动态代理
//需要引入Jar包--cglib 本案例使用cglib3.3.0
System.out.println("-+-+-+使用基于子类的代理-+-+-+");
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "F:\\09 个人学习\\com\\sun\\proxy");
final BuyCarImpl car=new BuyCarImpl(); //目标类
//生成代理对象
Object o = new DynamicSubClass(car).getProxyInstance();
BuyCar buyCar=(BuyCar) o;
buyCar.buyCar();
}
}
运行时代理对象的类型
拓展
JDK生成的字节码文件
看源码得知,代理文件继承了Proxy,实现了BuyCar接口,由于Java是单继承,所以JDK的动态代理只能基于接口实现
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.sun.proxy;
import com.promsing.porxy.v2.BuyCar;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements BuyCar {
private static Method m1;//equals
private static Method m2;//tostring
private static Method m3;//buyCar
private static Method m0;//hascode
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 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 void buyCar() throws {
try {
//h,就是切面类(代理类)
super.h.invoke(this, m3, (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"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.promsing.porxy.v2.BuyCar").getMethod("buyCar");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
Cglib生成的字节码文件
看源码得知,代理文件继承了BuyCarImpl,所以cglib是基于子类的动态代理
public class BuyCarImpl$$EnhancerByCGLIB$$1caed7b0 extends BuyCarImpl implements Factory {
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class var0 = Class.forName("com.promsing.structure.proxy.v2.BuyCarImpl$$EnhancerByCGLIB$$1caed7b0");
Class var1;
Method[] var10000 = ReflectUtils.findMethods(new String[]{
"finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
CGLIB$finalize$1$Method = var10000[0];
CGLIB$finalize$1$Proxy = MethodProxy.create(var1, var0, "()V", "finalize", "CGLIB$finalize$1");
CGLIB$equals$2$Method = var10000[1];
CGLIB$equals$2$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");
CGLIB$toString$3$Method = var10000[2];
CGLIB$toString$3$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");
CGLIB$hashCode$4$Method = var10000[3];
CGLIB$hashCode$4$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$4");
CGLIB$clone$5$Method = var10000[4];
CGLIB$clone$5$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
CGLIB$buyCar$0$Method = ReflectUtils.findMethods(new String[]{
"buyCar", "()V"}, (var1 = Class.forName("com.promsing.structure.proxy.v2.BuyCarImpl")).getDeclaredMethods())[0];
CGLIB$buyCar$0$Proxy = MethodProxy.create(var1, var0, "()V", "buyCar", "CGLIB$buyCar$0");
}
final void CGLIB$buyCar$0() {
super.buyCar();
}
//buyCar的方法
public final void buyCar() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$buyCar$0$Method, CGLIB$emptyArgs, CGLIB$buyCar$0$Proxy);
} else {
super.buyCar();
}
}
·
final boolean CGLIB$equals$2(Object var1) {
return super.equals(var1);
}
//equals
public final boolean equals(Object var1) {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
Object var2 = var10000.intercept(this, CGLIB$equals$2$Method, new Object[]{
var1}, CGLIB$equals$2$Proxy);
return var2 == null ? false : (Boolean)var2;
} else {
return super.equals(var1);
}
}
final String CGLIB$toString$3() {
return super.toString();
}
//tostring
public final String toString() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
return var10000 != null ? (String)var10000.intercept(this, CGLIB$toString$3$Method, CGLIB$emptyArgs, CGLIB$toString$3$Proxy) : super.toString();
}
final int CGLIB$hashCode$4() {
return super.hashCode();
}
//hashCode
public final int hashCode() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
Object var1 = var10000.intercept(this, CGLIB$hashCode$4$Method, CGLIB$emptyArgs, CGLIB$hashCode$4$Proxy);
return var1 == null ? 0 : ((Number)var1).intValue();
} else {
return super.hashCode();
}
}
final Object CGLIB$clone$5() throws CloneNotSupportedException {
return super.clone();
}
protected final Object clone() throws CloneNotSupportedException {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
return var10000 != null ? var10000.intercept(this, CGLIB$clone$5$Method, CGLIB$emptyArgs, CGLIB$clone$5$Proxy) : super.clone();
}
public static MethodProxy CGLIB$findMethodProxy(Signature var0) {
String var10000 = var0.toString();
switch(var10000.hashCode()) {
case -1574182249:
if (var10000.equals("finalize()V")) {
return CGLIB$finalize$1$Proxy;
}
break;
case -1261565625:
if (var10000.equals("buyCar()V")) {
return CGLIB$buyCar$0$Proxy;
}
break;
case -508378822:
if (var10000.equals("clone()Ljava/lang/Object;")) {
return CGLIB$clone$5$Proxy;
}
break;
case 1826985398:
if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
return CGLIB$equals$2$Proxy;
}
break;
case 1913648695:
if (var10000.equals("toString()Ljava/lang/String;")) {
return CGLIB$toString$3$Proxy;
}
break;
case 1984935277:
if (var10000.equals("hashCode()I")) {
return CGLIB$hashCode$4$Proxy;
}
}
return null;
}
public BuyCarImpl$$EnhancerByCGLIB$$1caed7b0() {
CGLIB$BIND_CALLBACKS(this);
}
public Object newInstance(Callback var1) {
CGLIB$SET_THREAD_CALLBACKS(new Callback[]{
var1});
BuyCarImpl$$EnhancerByCGLIB$$1caed7b0 var10000 = new BuyCarImpl$$EnhancerByCGLIB$$1caed7b0();
CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
return var10000;
}
public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) {
CGLIB$SET_THREAD_CALLBACKS(var3);
BuyCarImpl$$EnhancerByCGLIB$$1caed7b0 var10000 = new BuyCarImpl$$EnhancerByCGLIB$$1caed7b0;
switch(var1.length) {
case 0:
var10000.<init>();
CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
return var10000;
default:
throw new IllegalArgumentException("Constructor not found");
}
}
}
jdk的动态代理与cglib动态代理的底层实现
1.JDK动态代理是实现了被代理对象的接口,Cglib是继承了被代理对象。
2.JDK和Cglib都是在运行期生成字节码,JDK是直接写成代理对象Class字节码,Cglib使用ASM框架写Class字节码,Cglib代理实现更复杂,生成代理类比JDK效率低。
3.JDK调用代理方法,是通过反射机制调用,Cglib是通过FastClass机制直接调用方法,Cglib执行效率更高。
4.cglib封装了ASM这个开源框架,对字节码操作,完成对代理类的创建.主要通过集成目标对象,然后完成重写,再操作字节码
ASM 是一个 Java 字节码操控框架。它能够以二进制形式修改已有类或者动态生成类。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。
FastClass 的原理简单来说就是:为不需要反射 invoke 调用的原类型生成一个 FastClass 类,然后给原类型的方法分配一个 index,在生成的 FastClass 中的 invoke 方法中,先直接把 Object 强制转换为原类型,然后根据这个 index,就可以直接定位要调用的方法直接进行调用,这样省去了反射调用,所以调用效率比 JDK 动态代理通过反射调用高。
拓展:https://blog.csdn.net/z69183787/article/details/106878203/
若动态代理要对目标类的增强逻辑进行拓展,结合策略模式,只需要新增策略类,无需修改代理类代码
Spring 中何时使用 JDK 或 CGLIB
如果目标对象实现了接口,默认情况下会采用 JDK 的动态代理实现 AOP
如果目标对象实现了接口,也可以强制使用 CGLIB 实现 AOP
如果目标对象没有实现接口,必须采用 CGLIB,Spring 会自动在 JDK 动态代理和 CGLIB 之间转换
答案:DefaultAopProxyFactory类中有源码
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
//JDK动态代理和CGlib的选择,判断是否配置,判断是否是接口
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
/**
* Determine whether the supplied {@link AdvisedSupport} has only the
* {@link org.springframework.aop.SpringProxy} interface specified
* (or no proxy interfaces specified at all).
*/
private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
Class<?>[] ifcs = config.getProxiedInterfaces();
return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
}
}