代理模式

前言

代理类主要目的在于隔离目标类,可以在代理类写公用的代码比如日志,也可以加入一些缓存。也能起到降低耦合度的效果。
有时候我们在不想修改目标的类的情况下修改逻辑。也在增加代理类。

代理类的使用主要分为2种方式:静态代理、动态代理

静态代理

我们以买电脑为例子

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
// 目标接口
public interface IComputerStore {
String buyComputer();
}
// 目标接口的具体实现类
public class ComputerStore implements IComputerStore{
@Override
public String buyComputer() {
return "mac book";
}
}
// 静态代理类
public class ComputerStoreStaticProxy implements IComputerStore{
private IComputerStore computerStore;
public ComputerStoreStaticProxy(IComputerStore computerStore) {
this.computerStore = computerStore;
}
@Override
public String buyComputer() {
System.out.println("收钱");
String computer = computerStore.buyComputer();
System.out.println("发货");
return computer;
}
}
// test
public class Main {
public static void main(String[] args) {
IComputerStore computerStore = new ComputerStore();
IComputerStore computerStoreStaticProxy = new ComputerStoreStaticProxy(computerStore);
computerStoreStaticProxy.buyComputer();
}
}

以上就是一个非常简单的静态代理模式,代码比较简单自己看吧。

这个写法缺点也比较明显,代理都是手工编写。不利于管理,而且如果类比较多,工作量大也比较大

动态代理

JDK动态代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 编写动态代理
public class JdkProxyHandler implements InvocationHandler {
private Object object;
public JdkProxyHandler(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("收钱");
Object result = method.invoke(object, args);
System.out.println("拿到电脑");
System.out.println("发货");
return result;
}
}
// JDK动态代理的调用
public static void main(String[] args) throws Exception {
IComputerStore computerStore = new ComputerStore();
IComputerStore proxy = (IComputerStore)Proxy.newProxyInstance(ComputerStore.class.getClassLoader(),
ComputerStore.class.getInterfaces(), new JdkProxyHandler(computerStore));
proxy.buyComputer();

}

JDK生成的代理类都是继承Proxy且生成的代理类和委托类实现同一个接口,而且实现了InvocationHandler。
通过调用invoke方法。来实现目标类的访问。

jdk动态代理只能代理接口

cglib代理

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
// 编写动态代理
public class CglibProxyHandle implements MethodInterceptor {

private Object obj;

public Object getInstance(Object obj) {
this.obj = obj;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.obj.getClass());
// 设置回调方法
enhancer.setCallback(this);
// 创建代理对象
return enhancer.create();
}

@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("收钱");
Object res = methodProxy.invokeSuper(o, objects);
System.out.println("拿到电脑");
System.out.println("发货");
return res;
}
}
// cglib代理的调用
public static void main(String[] args) throws Exception {
CglibProxyHandle cglibProxyHandle = new CglibProxyHandle();
IComputerStore instance = (IComputerStore)cglibProxyHandle.getInstance(new ComputerStore());
instance.buyComputer();
}

注意cdlib生成的代理类是继承目标类的,实现接口Factory。所以可以代理普通类,但是方法不能是final,因为final 不能重写。
fastClass机制所以执行效率要比jdk快,jdk通过反射机制来调用目标类

比较

对三种代理进行一个简单的比较过程

代理 实现方式 优点 缺点 特点
静态代理 代理类和委托类实现一个接口,硬编码 简单,容易懂 硬编码不好管理,代码量大 -
JDK动态代理 代理和委托类实现同一个接口,通过实现InvocationHandler重写invoke来实现访问委托类 复用度高,JDK自带 只能代理委托类实现的接口 底层通过反射机制来调用目标方法
cglib动态代理 代理通过将委托类作为父类。同时为其委托方法创建两个方法,一个是与委托方法签名相同的方法,它在方法中会通过super调用委托方法;另一个是代理类独有的方法。在代理方法中,它会判断是否存在实现了MethodInterceptor接口的对象,若存在则将调用intercept方法对委托方法进行代理 可以在运行时对类或者是接口进行增强操作,且委托类无需实现接口 不能对final类和final方法进行代理 底层将方法全部存入一个数组中,通过数组索引直接进行方法调用,调用效率比JDK高

本篇文章主要是结论性的东西,便于自己学习