代理模式
-
一种设计模式
-
简单地说,在代理模式中存在三个角色
-
用户
-
代理
-
被代理的对象
-
-
用户调用代理,代理去调用被代理的对象
-
以此来实现功能的增强
-
动态代理在java中有两种实现方法
-
JDK中的Proxy类
-
CGLIB
-
JDK中的Proxy类
步骤
-
实现InvocationHandler接口,创建自己的调用处理器
-
通过为Proxy类指定ClassLoader和一组Interface来创建动态代理类
- 被代理对象的ClassLoader和Interface
-
通过反射机制获取动态代理类的构造函数
- 其需要的唯一参数类型是InvocationHandler
-
通过构造函数创建动态代理实例
- 构造时将之前实现的InvocationHandler对象作为参数传入
这四步之后,我们就可以用使用被代理对象的方式,来使用动态代理实例了
另外
-
后三步可以自己手动调用Proxy类的方法来分别实现
-
也可以直接调用Proxy封装好的方法来一步实现
Proxy.newProxyInstance(ClassLoader, Interface[], InvocationHandler)
Demo
package cn.andl;
import cn.andl.util.Computer;
import cn.andl.util.impl.ComputerImpl;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 测试JDK动态代理方式
* @author Andl
* @since 2023/5/29 11:17
*/
public class TestProxy {
public static void main(String[] args) {
// 创建被代理对象实例
ComputerImpl computer = new ComputerImpl();
// 实现一个调用处理器
InvocationHandler invocationHandler = new InvocationHandler() {
/**
* 在之后的代理类调用方法时,会实际调用这个方法
*
* @param proxy 代理
*
* @param method 要被代理的方法
*
* @param args 方法里的参数列表
*
* @return 方法的返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.format("[%s] 执行 %s 方法, 参数1:%d, 参数2:%d\n",
this.getClass().getName(), method.getName(), (Integer)args[0], (Integer)args[1]);
// 调用被代理对象的方法,并获取返回值
Object result = method.invoke(computer, args);
System.out.format("[%s] 执行 %s 方法完毕, 结果:%d\n",
this.getClass().getName(), method.getName(), (Integer)result);
return result;
}
};
//获取代理对象
Computer computerProxy = (Computer) Proxy.newProxyInstance(
// 被代理对象的类加载器
computer.getClass().getClassLoader(),
// 被代理对象实现的接口
computer.getClass().getInterfaces(),
// 调用处理器
invocationHandler);
// 执行方法
computerProxy.add(1, 2);
}
}
Computer接口
package cn.andl.util;
/**
* 计算接口
* @author Andl
* @create 2023/5/29 11:18
*/
public interface Computer {
/**
* 计算a和b的和
* @param a 加数1
* @param b 加数2
* @return 和
*/
int add(int a, int b);
}
ComputerImpl类
package cn.andl.util.impl;
import cn.andl.util.Computer;
/**
* 计算接口实现类
* @author Andl
* @since 2023/5/29 11:23
*/
public class ComputerImpl implements Computer {
@Override
public int add(int a, int b) {
System.out.format("[%s] 方法执行中\n", this.getClass().getName());
return a + b;
}
}
输出结果
[cn.andl.TestProxy$1] 执行 add 方法, 参数1:1, 参数2:2
[cn.andl.util.impl.ComputerImpl] 方法执行中
[cn.andl.TestProxy$1] 执行 add 方法完毕, 结果:3:3
Demo2
简单封装一下
package cn.andl;
import cn.andl.util.Computer;
import cn.andl.util.impl.ComputerImpl;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 测试JDK动态代理2
* @author Andl
* @since 2023/5/29 13:19
*/
public class TestProxy2 {
static class InvocationHandlerImpl implements InvocationHandler {
Object originalObject;
public Object bind(Object originalObject) {
this.originalObject = originalObject;
return Proxy.newProxyInstance(
originalObject.getClass().getClassLoader(),
originalObject.getClass().getInterfaces(),
this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.format("[%s] 执行 %s 方法, 参数1:%d, 参数2:%d\n",
this.getClass().getName(), method.getName(), (Integer)args[0], (Integer)args[1]);
Object result = method.invoke(originalObject, args);
System.out.format("[%s] 执行 %s 方法完毕, 结果:%d\n",
this.getClass().getName(), method.getName(), (Integer)result);
return result;
}
}
public static void main(String[] args) {
// 获取代理
Computer computer = (Computer) new InvocationHandlerImpl().bind(new ComputerImpl());
// 执行方法
computer.add(1, 2);
}
}
输出结果
[cn.andl.TestProxy2$InvocationHandlerImpl] 执行 add 方法, 参数1:1, 参数2:2
[cn.andl.util.impl.ComputerImpl] 方法执行中
[cn.andl.TestProxy2$InvocationHandlerImpl] 执行 add 方法完毕, 结果:3
原理简述
通过在main方法中最开始时加入一句代码,我们可以保留动态代理对象的字节码文件
-
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
-
可以在和src同级的com文件夹下的
sun/proxy/
中找到
类名
public final class $Proxy0 extends Proxy implements Computer {
观察类名可以发现,动态代理类继承了Proxy方法,实现了Computer接口
静态代码块
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("cn.andl.util.Computer").getMethod("add", Integer.TYPE, Integer.TYPE);
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
观察静态代码块可以发现,被代理对象的方法被赋值到了变量中
add方法
public final int add(int var1, int var2) throws {
try {
return (Integer)super.h.invoke(this, m3, new Object[]{var1, var2});
} catch (RuntimeException | Error var4) {
throw var4;
} catch (Throwable var5) {
throw new UndeclaredThrowableException(var5);
}
}
观察动态代理类中的add方法可以发现
-
是通过调用父类中变量
h
的invoke方法来实现功能的 -
而这个
h
,就是我们在之前创建动态代理类时,向构造器传入的InvocationHandler
CGLIB
-
cglib是一个功能强大、高性能、高质量的字节码操作库
-
主要用于在运行时拓展Java类或者根据接口生成对象
-
本身的实现基于asm库
-
要使用cglib主要会用到Enhancer和回调类
Enhancer
-
Enhancer是cglib中使用最多的类
-
Enhancer可以生成被代理类的子类,并且会拦截所有方法的调用
- 称之为增强
-
Enhancer可以基于接口来生成动态代理类,也可以直接基于类生成动态代理类
-
Enhancer不能增强构造函数,也不能增强被final修饰的类,或者被static和final修饰的方法
- 因为Enhancer是通过继承被代理的目标类来是实现增强的
-
Enhancer的使用分成两步
-
传入目标类型
-
设置回调
-
MethodInterceptor
cglib中回调类型有很多,这里主要介绍方法拦截器MethodInterceptor
-
方法拦截器会对被代理的目标类中所有可以增强的方法进行增强
- 不包括构造方法、final方法和static方法
-
方法拦截器的核心方法
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
-
o
- 被代理的目标对象
-
method
- 被代理的目标方法
-
objects
- 参数列表
-
methodProxy
- 代理类的方法引用
-
Demo
public class TestCGLIB {
public static void main(String[] args) {
// 初始化enhancer对象
Enhancer enhancer = new Enhancer();
// 传入目标类型
enhancer.setSuperclass(ComputerImpl.class);
// 也可以传入接口
// enhancer.setInterfaces(ComputerImpl.class.getInterfaces());
// 设置回调类型
enhancer.setCallback(new MethodInterceptor() {
/**
* 拦截方法
* @param o 目标对象
* @param method 目标方法
* @param objects 参数列表
* @param methodProxy 代理方法
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.format("[%s] 执行方法 [%s] 参数:[%d][%d]\n",
this.getClass().getName(), method.getName(), (Integer)objects[0], (Integer)objects[1]);
Object result = methodProxy.invokeSuper(o, objects);
System.out.format("[%s] 执行方法 [%s] 结果:[%d]\n",
this.getClass().getName(), method.getName(), (Integer)result);
return result;
}
});
// 创建代理对象
ComputerImpl computer = (ComputerImpl)enhancer.create();
// 执行方法
computer.add(1, 2);
}
}
标签:andl,Java,cn,Object,代理,笔记,方法,public
From: https://www.cnblogs.com/Andl-Liu/p/17502124.html