0%

Java代理模式

前言

代理模式分为静态代理和动态代理,动态代理是一种常见的设计模式。

目录

一、什么是代理模式

代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活 中常见的中介。代理模式的目的:

  • 通过引入代理对象的方式来间接访问目标对象,防止直接访问目标对象给系统带来的不必要复杂性。

  • 通过代理对象对访问进行控制。

代理模式一般会有三个角色:

抽象角色:指代理角色和真实角色对外提供的公共方法,一般为一个接口。

真实角色:需要实现抽象角色接口,定义了真实角色所要实现的业务逻辑,以便供代理角色调用。也就是真正的业务逻辑在此。

代理角色:需要实现抽象角色接口,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附 加自己的操作。将统一的流程控制都放到代理角色中处理。

二、静态代理

静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。一般来说,被代理对象和代理对象是一对一的关系,当然一个代理对象对应多个被代理对象也是可以的。

静态代理,一对一则会出现时静态代理对象量多、代码量大,从而导致代码复杂,可维护性差的问题,一对多则代理对象会出现扩展能力差的问题。

三、动态代理

在运行时再创建代理类和其实例,因此显然效率更低。要完成这个场景,需要在运行期动态创建一个Class。JDK提供了 Proxy 来完成这件事情。

如何使用

基本使用如下:

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
//抽象角色 
interface Api {
void test(String a);
}

//真实角色
class ApiImpl{
@Override
public void test(String a) { System.out.println("真实实现:" + a);}
}

//创建真实角色实例
ApiImpl api = new ApiImpl();

//动态代理类
class MarkCompany implements InvocationHandler{
/*持有的真实对象*/
private Object factory;

public Object getFactory() {
return factory;
}

public void setFactory(Object factory) {
this.factory = factory;
}

/*通过Proxy获得动态代理对象*/
public Object getProxyInstance(){
return Proxy.newProxyInstance(factory.getClass().getClassLoader(),
factory.getClass().getInterfaces(),this);
}

/*通过动态代理对象方法进行增强*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
doSthAfter();
Object result = method.invoke(factory, args);
doSthBefore();
return result;
}

/*前置处理器*/
private void doSthAfter() {
}
/*后置处理器*/

private void doSthBefore() {
}
}

public class Client {
public static void main(String[] args) {
/*静态代理模式---------------*/
// AInterface afactory = new AaFactory();
// AProxy aProxy = new AProxy(afactory);//new一个代理类
// aProxy.testA();
//
// BInterface bFactory = new BbFactory();
// BProxy bProxy = new BProxy(bFactory);//new一个代理类
// bProxy.testB();

/*动态代理模式---------------*/
AInterface aFactory = new AaFactory();//A接口的Impl
MarkCompany markCompany = new MarkCompany();
markCompany.setFactory(afactory);
AInterface aProxy = (AInterface)markCompany.getProxyInstance();
aProxy.testA();

BInterface bFactory = new BbFactory();//B接口的Impl
markCompany.setFactory(bFactory);
BInterface bProxy = (BInterface)markCompany.getProxyInstance();
bProxy.testB();
}
}

实际上,Proxy.newProxyInstance 会创建一个Class,与静态代理不同,这个Class不是由具体的.java源文件编译而来,即没有真正的文件,只是在内存中按照Class格式生成了一个名为proxyPkg + $Proxy + num的class文件,由源码可知是生成了一个byte数组。

InvocationHandler 接口,在使用动态代理时,传递的 InvocationHandler 就是一个监听,在代理对象上执行方法,都会由这个监听回调出来。

InvocationHandler接口是给动态代理类实现的,负责处理被代理对象的操作的,而Proxy是用来创建动态代理类实例对象的,因为只有得到了这个对象我们才能调用那些需要代理的方法。

实现原理

观察Proxy.newProxyInstance()方法,与创建对象有关的代码主要有:

1
2
3
4
5
6
7
8
9
10
11
12
13
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) throws IllegalArgumentException
{
//复制一份接口
final Class<?>[] intfs = interfaces.clone();
// 获得代理类的class对象
Class<?> cl = getProxyClass0(loader, intfs);
// 获得代理类的构造器
final Constructor<?> cons = cl.getConstructor(constructorParams);
//创建代理类的实例
...
return cons.newInstance(new Object[]{h});

其中的关键点就是如何获得代理类的class对象,我们进入getProxyClass0()方法

1
2
3
4
5
6
7
8
9
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
//对接口方法限制数量65535
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
//JDK内部使用了某种机制缓存了我们的代理类的class对象
return proxyClassCache.get(loader, interfaces);
}

JDK内部使用WeakCache缓存了代理类的class对象,同时用到了被代理类的类加载器和类实现的接口这两个参数的是apply方法

1
2
3
4
5
6
7
// WeakCache.java
public V get(K key, P parameter) {

// create subKey and retrieve the possible Supplier<V> stored by that
// subKey from valuesMap
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
}

subKeyFactory.apply具体实现是Proxy.ProxyClassFactory

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
// Proxy.java   
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
// prefix for all proxy class names
private static final String proxyClassNamePrefix = "$Proxy";

// next number to use for generation of unique proxy class names
private static final AtomicLong nextUniqueNumber = new AtomicLong();

@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
/*
* Choose a name for the proxy class to generate.
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;

return generateProxy(proxyName, interfaces, loader, methodsArray,
exceptionsArray);
}
}

//调用native方法
private static native Class<?> generateProxy(String name, Class<?>[] interfaces,
ClassLoader loader, Method[] methods,Class<?>[][] exceptions);

动态代理类class的文件在内存中是byte数组形式存在,通过ProxyGenerator.generateProxyClass()生成的

1
2
3
4
5
6
7
// ProxyGenerator.class 
public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
final byte[] var4 = var3.generateClassFile();
...
return var4;
}
优缺点

优点:只需要一个动态代理类就可以解决创建多个静态代理的问题,避免重复、多余代码,更强的灵活性。

缺点:效率较低,相比静态代理中直接调用目标对象方法,动态代理则需要先通过Java反射机制间接调用目标对象方法。

应用场景局限,因为 Java 的单继承特性(每个代理类都继承了 Proxy 类),即只能针对接口创建代理类,不能针对类创建代理类。

总结