Java Proxy
代理模式实际上是基本等同于装饰器模式,其目的是避免用户对实体的直接访问,这样我们就可以在用户和被访问的实体之间创建一位代理,这样我们就能在用户访问实体时 做一些控制。
代理模式非常有用,比如我们希望能在用户访问时能记录日志,或者能校验用户是否拥有权限,或者不允许用户调用一些方法,或者我们调用方法时实际上会调用远程 服务器上的某个对象的方法(RMI)。
代理模式的玩法一般是:首先创建接口,而用户仅通过接口来使用被代理对象,从而解耦用户和被代理对象。之后我们让被代理对象和代理均实现接口,之后用代理对象 持有被代理对象并返回给用户。注意代理如果以接口持有被代理对象,那么还能解耦代理和被代理对象。
class Mother{
//访问Mary并翻翻她的日记
public void visit(Mary mary){
Diary diary = mary.provide();
String page = diary.get(...);
}
}
class Mary{
Diary diary = new DairyImpl();
//给妈妈自己的日记
public Diary provide(){
return new DiaryProxy(diary);
}
}
interface Diary{
String getPage(int index);
}
class DairyImpl implements Diary{
public String getPage(int index){...}
}
class DiaryProxy implements Diary{
Diary inner;
public DiaryProxy(Diary inner){
this.inner = inner;
}
public String getPage(int index){
if(不希望妈妈看到页index){
return "Nothing";
}
return inner.get(index);
}
}
java中提供了多种代理的机制,下面仅讨论两种。
JDK自带的Proxy
JDK中自带了Proxy,我们需要创建一个InvocationHandler,并让Proxy为我们创建实现若干特定接口的代理,之后代理的任意方法被调用时,都会转发到我们的 InvocationHandler进行处理。
final Runnable task = new Runnable() {
@Override
public void run() {
System.out.println("run...");
}
};
InvocationHandler proxy = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("logging at " + new Date());
System.out.println("invoke method " + method.getName() + " with args " + Arrays.toString(args));
Object ret = method.invoke(task, args);
System.out.println("return " + ret);
return ret;
}
};
Runnable proxyTask = (Runnable) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{Runnable.class}, proxy);
proxyTask.run();
cglib的Proxy
JDK自带的代理方法有如下的问题:
- 使用反射,导致性能下降。
- 无法代理实现类。
而cglib则利用动态字节码(asm)技术,规避了上面提到的两个问题。
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Object.class);
Object object = new CGLibTest();
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println(MessageFormat.format("{0} is invoked with {1}", method, Arrays.toString(args)));
Object result = proxy.invoke(object, args);
System.out.println(MessageFormat.format("return {0}", result));
return result;
}
});
Object proxy = enhancer.create();
proxy.hashCode();
proxy.toString();
如果我们希望能创建多个代理对象,那么可以借助Factory接口。
enhancer.setUseFactory(true);
Factory proxyFactory = (Factory) enhancer.create();
Object proxy = proxyFactory.newInstance(interceptor);