设计模式:代理(Proxy) in java

什么是代理?

代理是指,本该有A做的工作,现在找一个代理人B,然后由B来进行实际的工作。
代理,简单来分,可以分为以下两类:

  • 静态代理
  • 动态代理

静态代理

以实现两个数的加法场景为例:

1
2
3
public interface IAdd {
public int add(int a, int b);
}

实现类:

1
2
3
4
5
6
public class Add implements IAdd {
@Override
public int add(int a, int b) {
return a + b;
}
}

直接使用的话:

1
2
Add add = new Add();
add.add(3, 14);

那么我想在执行加运算时,做一些其他操作怎么办,已有的类ADD无法改,没有源码。这时很容易想到的就是扩展:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class AddProxy implements IAdd {
private IAdd add;

public AddProxy(IAdd add) {
this.add = add;
}
@Override
public int add(int a, int b) {
System.out.println("...begin...");
int result = add.add(3, 14);
System.out.println("...end...");
return result;
}
}

这样做,没有修改已有的类,并且增加了一些操作,此处为一些提示信息。采用了组合的方式,实现了代理模式。具体使用时,直接使用AddProxy即可。

1
2
IAdd add = new AddProxy(new Add());
int result = add.add(3, 14);

此为静态代理也。

动态代理

动态代理,是指运行时动态的生成代理类,完成功能。静态代理中,显然AddProxy是编译期已知的了。实现方式,主要有两种:

  • JDK Proxy
  • Cglib Proxy

JDK Proxy

Java自身提供了相关的类,来实现动态代理。
首先要定义一个java.lang.reflect.InvocationHandler接口实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* @author shenyanchao
*/
public class AddInvocationHandler implements InvocationHandler {

private Object target;
//绑定要代理的目标类
public void bind(Object target) {
this.target = target;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("......begin....");
Object result = method.invoke(target, args);
System.out.println("......end....");
return result;
}
}

那么在具体使用时,代码如下:

1
2
3
4
5
6
AddInvocationHandler addHandler = new AddInvocationHandler();
IAdd add = new Add();
addHandler.bind(add);
IAdd addProxy = (IAdd) Proxy.newProxyInstance(
Add.class.getClassLoader(), Add.class.getInterfaces(), addHandler);
int jdkResult = addProxy.add(3, 14);

从代码可见,主要是通过Proxy.newProxyInstance来在运行时生成代理类。需要注意的是,第二个参数必须使用具体实现类Add来获得interfaces,也就是说其代理的类必须实现了接口。addHandler负责绑定要代理的target类,并调用invoke来增强Add功能。

Cglib Proxy

JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
要使用CgLib,首先要实现一个CallBack接口的类,由于本例是为了实现method的拦截,因此直接实现MethodInterceptor即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* @author shenyanchao
*/
public class AddInterceptor implements MethodInterceptor {

@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println("....begin....");
Object result = proxy.invokeSuper(obj, args);
System.out.println("....end....");
return result;
}
}

具体使用时:

1
2
3
4
5
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Add.class);
enhancer.setCallback(new AddInterceptor());
Add add = (Add) enhancer.create();
int result = add.add(3, 14);

通过Enhancer制定需要增强的类,并设置CallBack函数来实现代理与功能增强。