代理模式
什么是代理模式
Java代理模式是一种常用的设计模式,主要用于在不修改现有类代码的情况下,为该类添加一些新的功能或行为。代理模式涉及到一个代理类和一个被代理类(也称为目标对象)。代理类负责控制对目标对象的访问,并可以在访问前后添加一些额外的操作。
核心作用:
- 通过代理,控制对对象的访问。
- 可以详细控制访问某个(某类)对象的方法,在调用这个方法茜做前置处理,调用这个方法后做后置处理(即AOP的微观实现)。
核心角色:
- 抽象角色:定义代理角色和真实角色的公共对外方法
- 真实角色:实现抽象角色,定义真实角色所需要实现的业务逻辑,供代理角色调用(关注真正的业务逻辑)。
- 代理角色:实现抽象角色,是真实角色的代理,通过真实角色业务逻辑方法,来实现抽象角色,并可附加自己的操作。
应用场景:
- 安全代理:屏蔽对真实角色的直接访问
- 远程代理:通过代理类处理远程方法调用(RMI)
- 延迟代理:先加载轻量级代理对象,真正需要再加载真实对象。
Java代理模式主要分为两种:静态代理和动态代理。
静态代理
静态代理是在代码中手动定义代理类,代理类与目标对象实现相同的接口,并在代理类中持有目标对象的引用。
当调用代理类的方法时,代理类会在调用目标对象方法的前后添加一些额外的逻辑。
案例
明星个人接活,与代理人接活
明星个人接活:
- 面谈
- 合同起草
- 签字
- 前期准备
- 唱歌
- 收尾款
通过代理人接活:
- 面谈
- 合同起草
- 签字
- 前期准备
- 唱歌(明星负责)
- 收尾款
UML
1 提供一个抽象角色,定义代理角色和真实角色的公共对外方法。
2 定义真实角色,实现抽象角色的方法(当然真实角色也可以定义一些自己的方法)
3 定义一个代理角色,实现抽象角色方法,通过传入真实角色调用真实角色的方法
4 使用时,创建代理角色及真实角色,通过代理角色调用相应方法
实现代码
Start.java
// 抽象角色
// 明星:具备唱歌能力
public interface Start {
void sing();
}
RealStart.java
// 真实角色
// * 实现明星唱歌
public class RealStart implements Start{
@Override
public void sing() {
System.out.println("周杰伦唱歌");
}
}
ProxyStart.java
// 代理明星
// * 通过实现Star方法代理明星唱歌
public class ProxyStart implements Start{
// 代理明星
private Start start;
// 通过构造器传入设置代理明星
public ProxyStart(Start start) {
this.start = start;
}
// 重写唱歌,代理实现唱歌
@Override
public void sing() {
System.out.print("5. ");
start.sing();
}
public void interview(){
System.out.println("1. 面谈");
}
public void contractDrafting(){
System.out.println("2. 合同起草");
}
public void signature(){
System.out.println("3. 签字");
}
public void preliminaryPreparation(){
System.out.println("4. 前期准备");
}
public void closingPayment(){
System.out.println("6. 收尾款");
}
}
TestClient.java
public class TestClient {
public static void main(String[] args) {
// 创建真实角色
RealStart realStart = new RealStart();
// 创建代理角色
ProxyStart proxyStart = new ProxyStart(realStart);
// 方法调用
// 面谈
proxyStart.interview();
// 合同起草
proxyStart.contractDrafting();
// 签字
proxyStart.signature();
// 前期准备
proxyStart.preliminaryPreparation();
// 唱歌
proxyStart.sing();
// 收尾款
proxyStart.closingPayment();
}
}
执行结果:
JDK实现动态代理
动态代理是在运行时动态生成代理类。相对于静态代理,将抽象角色(接口)中声明的所有方法都被转移到调用处理器一个集中的地方中处理,这样可以更加灵活和统一的处理众多的方法。
通过JDK提供了java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来实现动态代理。
- java.lang.reflect.Proxy
作用:动态生成代理类和对象 - java.lang.reflect.InvocationHandler(处理器接口)
- 可以通过invoke方法实现对真实角色的代理访问。
- 每次通过Proxy生成代理类对象时都要指定对应的处理器对象。
UML
使用JD实现动态代理时,我们需要创建一个实现了InvocationHandler接口的处理器类,并在该类中实现invoke方法。
然后,我们可以使用Proxy.newProxyInstance方法创建一个代理类的实例。
当调用代理类的方法时,实际上会调用处理器类的invoke方法。
代码实现
Start.java
// 抽象角色
interface Start {
void sing();
}
RealStart.java
// 真实角色
// * 实现明星唱歌
public class RealStart implements Start {
@Override
public void sing() {
System.out.println("周杰伦唱歌");
}
}
StartHandle.java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
// JDK 实现动态代理
public class StartHandle implements InvocationHandler {
// 代理明星
private Start start;
// 通过构造器传入设置代理明星
public StartHandle(Start start) {
this.start = start;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理方法执行之前");
System.out.println("1. 面谈");
System.out.println("2. 合同起草");
System.out.println("3. 签字");
System.out.println("4. 前期准备");
System.out.print("5. ");
method.invoke(start,args);
System.out.println("代理方法执行之后");
System.out.println("6. 收尾款");
return true;
}
}
TestClient.java
import java.lang.reflect.Proxy;
// 动态代理测试
public class TestClient {
public static void main(String[] args) {
// 创建真实角色
RealStart realStart = new RealStart();
// 创建动态代理处理类
StartHandle startHandle = new StartHandle(realStart);
Start proxy = (Start) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Start.class}, startHandle);
// 处理业务
proxy.sing();
}
}
执行结果:
通过接口实现代理
UML
1 定义一个抽象角色Start,接口中定义抽象sing方法
2 真实角色实现Star接口,实现sing方法
3 定义一个中转处理接口MethodHandler,接口中定义invoke方法(后续处理,后续代理类方法全部先调用该方法进行处理)
4 定义处理类(具体实现)StartMethodHandle,实现invoke方法,通过反射调用真实角色对应方法
5 代理对象StartProxy,实现Start接口,重写接口,所有接口调用处理类(具体实现)的invoke方法,并传入真实角色
注意:JDK 实现代理的逻辑与其类似
具体实现
Start.java
// 抽象角色
// * 明星:具备唱歌能力
public interface Start {
void sing() throws Throwable;
}
RealStart.java
// 真实角色
// * 实现明星唱歌
public class RealStart implements Start {
@Override
public void sing() throws Throwable {
System.out.println("周杰伦唱歌");
}
}
MethodHandler.java
// 中转处理接口
public interface MethodHandler {
Object invoke(Object self, String methodName, Object[] args) throws Throwable;
}
StartMethodHandle.java
import java.lang.reflect.Method;
// 处理类(具体实现)
public class StartMethodHandle implements MethodHandler {
@Override
public Object invoke(Object self, String methodName, Object[] args) throws Throwable {
System.out.println("代理执行之前");
Method method = self.getClass().getMethod(methodName);
Object invoke = method.invoke(self, args);
System.out.println("代理执行之后");
return invoke;
}
}
StartProxy.java
// 代理对象
public class StartProxy implements Start {
private MethodHandler methodHandler;
private Start start;
public StartProxy(MethodHandler methodHandler, Start start) {
this.methodHandler = methodHandler;
this.start = start;
}
@Override
public void sing() throws Throwable {
methodHandler.invoke(start,"sing",null);
}
}
TestClient3.java
public class TestClient3 {
public static void main(String[] args) {
// 创建真实角色
Start start = new RealStart();
// 创建代理处理类
StartMethodHandle startMethodHandle = new StartMethodHandle();
// 创建代理对象
StartProxy startProxy = new StartProxy(startMethodHandle, start);
try {
startProxy.sing();
} catch (Throwable e) {
e.printStackTrace();
}
}
}
执行结果:
javassist字节码实现动态代理
Javassist 是一个开源的分析、编辑和创建 Java 字节码的库。它主要用于操作 Java 字节码,但也可以用来创建动态代理。
需要引入依赖如下
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.27.0-GA</version>
</dependency>
实现代码
Start.java
// 抽象角色
// * 明星:具备唱歌能力
public interface Start {
void sing();
}
RealStart.java
/**
* 真实角色
* 实现明星唱歌
*
* @author Anna.
* @date 2024/4/9 10:10
*/
public class RealStart implements Start {
@Override
public void sing() {
System.out.println("周杰伦唱歌");
}
}
MethodHandler.java
// 中转处理接口
public interface MethodHandler {
Object invoke(Object self, String methodName, Object[] args) throws Exception;
}
StartMethodHandle.java
// 处理类
public class StartMethodHandle implements MethodHandler {
@Override
public Object invoke(Object self, String methodName, Object[] args) throws Exception {
System.out.println("代理执行之前");
Method method = self.getClass().getMethod(methodName);
Object invoke = method.invoke(self, args);
System.out.println("代理执行之后");
return invoke;
}
}
JavassistProxyFactory.java
import javassist.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class JavassistProxyFactory {
/**
* 创建代理类:相当于实现demo3中StartProxy
*
* @param target 真实角色
* @param methodHandler 处理类(具体实现)
* @return T
* @author Anna.
* @date 2024/4/9 17:26
*/
public static <T> T createProxy(T target, MethodHandler methodHandler) throws Exception {
// 创建 ClassPool,它是 Javassist 的核心类,用于处理类
ClassPool pool = ClassPool.getDefault();
// 获取目标类的 CtClass 对象
CtClass ctClass = pool.get(target.getClass().getName());
// 获取目标类的 CtClass 对象
CtClass methodHandlerCtClass = pool.get(methodHandler.getClass().getName());
// 创建代理类的名称
String proxyClassName = target.getClass().getName() + "$Proxy";
// 创建代理类的 CtClass 对象
CtClass proxyCtClass = pool.makeClass(proxyClassName);
// 设置代理类继承自目标类 用于实现其所有方法
proxyCtClass.setSuperclass(ctClass);
// 添加字段
CtField aField = new CtField(ctClass, "a", proxyCtClass);
aField.setModifiers(javassist.Modifier.PRIVATE);
CtField bField = new CtField(methodHandlerCtClass, "b", proxyCtClass);
bField.setModifiers(javassist.Modifier.PRIVATE);
// 添加属性
proxyCtClass.addField(aField);
proxyCtClass.addField(bField);
// 创建一个构造函数来初始化name属性
CtConstructor constructor = new CtConstructor(new CtClass[]{ctClass,methodHandlerCtClass}, proxyCtClass);
constructor.setModifiers(javassist.Modifier.PUBLIC);
// $0 this $1 第一个参数 $2 第二个参数 依次类推
constructor.setBody("{this.a = $1;this.b = $2;}");
proxyCtClass.addConstructor(constructor);
// 遍历目标类的所有方法,并创建相应的方法在代理类中
for (Method method : target.getClass().getDeclaredMethods()) {
// 创建方法
CtMethod ctMethod = new CtMethod(pool.get(method.getReturnType().getName()), method.getName(), getParameterTypes(method), proxyCtClass);
ctMethod.setModifiers(Modifier.PUBLIC);
// 设置方法体,调用 MethodHandler 的 invoke 方法
ctMethod.setBody("{ b.invoke(a, \"" + method.getName() + "\", $args); }");
// 添加方法到代理类
proxyCtClass.addMethod(ctMethod);
}
// 反射
Class<?> aClass = proxyCtClass.toClass();
Constructor<?>[] constructors = aClass.getConstructors();
Object obj = constructors[0].newInstance(target, methodHandler);
// 创建代理类的实例
return (T) obj;
}
/**
* 获取方法参数列表
*
* @param method
* @return javassist.CtClass[]
* @author Anna.
* @date 2024/4/9 17:40
*/
private static CtClass[] getParameterTypes(Method method) throws NotFoundException {
CtClass[] ctClasses = new CtClass[method.getParameterTypes().length];
for (int i = 0; i < method.getParameterTypes().length; i++) {
ctClasses[i] = ClassPool.getDefault().get(method.getParameterTypes()[i].getName());
}
return ctClasses;
}
}
TestClient4.java
public class TestClient4 {
public static void main(String[] args) throws Exception {
Start realStart = new RealStart();
StartMethodHandle startMethodHandle = new StartMethodHandle();
Start proxy = (Start) JavassistProxyFactory.createProxy(realStart, startMethodHandle);
proxy.sing();
}
}
执行结果:
gitee源码
git clone https://gitee.com/dchh/JavaStudyWorkSpaces.git