Java动态代理
2022-04-25 11:17:12 # Java安全

代理模式

为其他对象提供一个代理以控制对某个对象的访问。代理类主要负责为委托了(真实对象)预处理消息、过滤消息、传递消息给委托类,代理类不现实具体服务,而是利用委托类来完成服务,并将执行结果封装处理。

静态代理

创建一个接口,然后创建被代理的类实现该接口并且实现该接口中的抽象方法。之后再创建一个代理类,同时使其也实现这个接口。在代理类中持有一个被代理对象的引用,而后在代理类方法中调用该对象的方法。

接口:

1
2
3
public interface HelloInterface {
void sayHello();
}

被代理类:

1
2
3
4
5
6
public class Hello implements HelloInterface{
@Override
public void sayHello() {
System.out.println("Hello proxy!");
}
}

代理类:

1
2
3
4
5
6
7
8
9
public class HelloProxy implements HelloInterface{
private HelloInterface helloInterface = new Hello();
@Override
public void sayHello() {
System.out.println("Before invoke sayHello" );
helloInterface.sayHello();
System.out.println("After invoke sayHello");
}
}

代理类调用:
被代理类被传递给了代理类HelloProxy,代理类在执行具体方法时通过所持用的被代理类完成调用。

1
2
3
4
5
6
7
8
9
public static void main(String[] args) {
HelloProxy helloProxy = new HelloProxy();
helloProxy.sayHello();
}

输出:
Before invoke sayHello
Hello proxy!
After invoke sayHello

使用静态代理很容易就完成了对一个类的代理操作。但是静态代理的缺点也暴露了出来:由于代理只能为一个类服务,如果需要代理的类很多,那么就需要编写大量的代理类,比较繁琐。

动态代理

利用反射机制在运行时创建代理类。接口、被代理类不变,我们构建一个handler类来实现InvocationHandler接口。

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
import proxy.UserService;
import proxy.UserServiceImpl;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class Client2 {
public static void main(String[] args) throws IllegalAccessException, InstantiationException {
// 设置变量可以保存动态代理类,默认名称以 $Proxy0 格式命名
//System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
// 1. 创建被代理的对象,UserService接口的实现类
HelloInterface hello = new Hello();
// 2. 获取对应的 ClassLoader
ClassLoader classLoader = userServiceImpl.getClass().getClassLoader();
// 3. 获取所有接口的Class,这里的UserServiceImpl只实现了一个接口UserService,
Class[] interfaces = hello.getClass().getInterfaces();
// 4. 创建一个将传给代理类的调用请求处理器,处理所有的代理对象上的方法调用
// 这里创建的是一个自定义的处理器,须传入实际的执行对象 hello
InvocationHandler helloHandler = new HelloHandler(hello);
/*
5.根据上面提供的信息,创建代理对象 在这个过程中,
a.JDK会通过根据传入的参数信息动态地在内存中创建和.class 文件等同的字节码
b.然后根据相应的字节码转换成对应的class,
c.然后调用newInstance()创建代理实例
*/
HelloInterface proxy = (HelloInterface) Proxy.newProxyInstance(classLoader, interfaces, helloHandler);
// 调用代理的方法
proxy.sayHello();
// 保存JDK动态代理生成的代理类,类名保存为 HelloProxy
// ProxyUtils.generateClassFile(proxy.getClass(), "HelloProxy");
}
}

image-20220425143318908

InvocationHandlerProxy的主要方法有

java.lang.reflect.InvocationHandler

Object invoke(Object proxy, Method method, Object[] args)定义了代理对象调用方法时希望执行的动作,用于集中处理在动态代理类对象上的方法调用

java.lang.reflect.Proxy

1
2
3
4
5
6
7
static InvocationHandler getInvocationHandler(Object proxy)用于获取指定代理对象所关联的调用处理器

static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)返回指定接口的代理类

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 构造实现指定接口的代理类的一个新实例,所有方法会调用给定处理器对象的 invoke 方法

static boolean isProxyClass(Class<?> cl)返回 cl 是否为一个代理类

动态代理具体步骤:

  1. 通过实现 InvocationHandler 接口创建自己的调用处理器;
  2. 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
  3. 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
  4. 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。
  5. 调用方法的时候通过 super.h.invoke(this, m1, (Object[])null); 调用,其中的 super.h.invoke 实际上是在创建代理的时候传递给 Proxy.newProxyInstanceHandler 对象,它继承 InvocationHandler 类,负责实际的调用处理逻辑。
  6. Handlerinvoke 方法接收到 method、args 等参数后,进行一些处理,然后通过反射让被代理的对象 target 执行方法。

参考

https://zhuanlan.zhihu.com/p/347141071?msclkid=0fbfe08dc46311ecae26c4e44894f809

https://www.jianshu.com/p/9bcac608c714?msclkid=b40c8c68c44511ecb19300522d8d7118