代理模式

一、概述

代理模式即在不改变原始类或叫被代理类代码的情况下,通过引入代理类来给原始类附加功能。

代理模式分为静态代理动态代理。静态代理需要针对每个类都创建一个代理类(每个代理类中的代码都有点像模板式的重复代码)增加了维护成本和开发成本。对于静态代理存在的问题,我们可以通过动态代理来解决。 动态代理即不事先为每个原始类编写代理类,而是在运行的时候,动态地创建原始类对应的代理类,然后在系统中用代理类替换掉原始类。 (动态代理底层依赖的就是 Java 的反射语法)

实际上,Spring AOP 底层的实现原理就是基于动态代理。用户配置好需要给哪些类创建代理,并定义好在执行原始类的业务代码前后执行哪些附加功能。Spring 为这些类创建动态代理对象,并在 JVM 中替代原始类对象。 原本在代码中执行的原始类的方法,被换作执行代理类的方法,也就实现了给原始类添加附加功能的目的。

二、 实现

1. Java动态代理

定义接口以及实现类

1
2
3
public interface MessageService {
String sendMessage();
}
1
2
3
4
5
6
7
8
public class MessageServiceImpl implements MessageService {

@Override
public String sendMessage() {
System.out.println("发送消息");
return "发送消息成功";
}
}

实现InvocationHandler接口

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;invoke方法中proxy为动态生成的代理类、method为代理类调用的方法对象,args为当前方法调用的参数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ServiceProxy implements InvocationHandler {

private Object target;

public ServiceProxy(Object target) {
this.target = target;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//添加的业务
System.out.println("代理类执行的操作");
Object result = method.invoke(target, args);
return result;
}

}

通过Proxy.newInstance()方法获得代理对象

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h)方法中loader为类加载器,用于加载代理对象,interfaces为被代理对象实现的接口,h为实现InvocationHanlder接口的对象。

1
2
3
4
5
6
7
8
9
public class ServiceProxyFactory {
//获取代理对象
public static Object createProxy(Object target) {
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new ServiceProxy(target));
}

}

运行程序,程序执行结果如下图

1
2
MessageService service= (MessageService) ServiceProxyFactory.createProxy(new MessageServiceImpl());
System.out.println(service.sendMessage());

image-20230129214902576

2. CGLIB 动态代理机制

引入cglib的jar包

1
2
3
4
5
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>

被代理类的实现

1
2
3
4
5
6
public class MessageService {
public String sendMessage() {
System.out.println("发送消息");
return "发送消息成功";
}
}

实现MethodInterceptor接口中的intercept方法,创建代理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class MessageIntercept implements MethodInterceptor {

//获取代理类
public static <T> T getProxy(Class<T> clazz) {
//创建增强类
Enhancer enhancer = new Enhancer();
//设置类加载器
enhancer.setClassLoader(clazz.getClassLoader());
//设置被代理类
enhancer.setSuperclass(clazz);
//设置方法拦截器
enhancer.setCallback(new MessageIntercept());
//创建代理类
return (T) enhancer.create();
}


/**
* 增强被代理类的方法
* @param o 被代理的对象
* @param method 被拦截的方法
* @param objects 方法的参数
* @param methodProxy 用于调用原始方法
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("代理类执行的操作");
Object obj = methodProxy.invokeSuper(o, objects);
return obj;
}

public static void main(String[] args) {
MessageService service = getProxy(MessageService.class);
System.out.println(service.sendMessage());
}
}

Java动态代理和CGLIB的对比

  1. Java动态代理只能代理实现了接口的类或直接代理接口,CGLIB可以代理未实现任何接口的类。
  2. Java动态代理通过反射机制实现一个代理接口的匿名类在调用具体方法前用InvokeHandler处理,cglib利用ASM开源包,通过修改被代理对象的class文件生成子类来处理。

CGLIB 动态代理是通过生成一个被代理类的子类来拦截被代理类的方法调用,因此不能代理声明为 final 类型的类和方法。

ASM: 一个 Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。

Java中,动态代理的实现基于字节码生成技术,可以在jvm运行时动态生成和加载字节码并加载到JVM中,而静态代理在编译期则将接口、实现类、代理类编译为class文件

三、应用场景

  1. 业务系统的非功能性需求开发:代理模式最常用的一个应用场景就是,在业务系统中开发一些非功能性需求,比如:监控、统计、鉴权、限流、事务、幂等、日志。

  2. 代理模式在 RPC、缓存中的应用,我自己写的RPC项目中就用到了代理模式,目的是为了让RPC的使用者不用关注客户端交互的细节,只需要开发业务逻辑,就像使用本地方法一样。

参考资料

  1. 代理模式

  2. Java 代理模式详解


代理模式
https://pursuemilk.github.io/2023/01/31/代理模式/
作者
PursueMilk
发布于
2023年1月31日
许可协议