改进(需求)
我们的静态代理,只能代理一个固定的接口。
比如在(二)中,我们写了4个类去实现Movable接口,也就是4个类代理一个类。
那我如果现在多写一个Eatable接口,那我又需要再写4个类去代理它(假如要实现的功能与上面类似时)。
那这样似乎又是在重复堆砌了,能否有一种把所要代理的接口写活的方法呢?
让我们只需要写一次代理类,处处使用?
设计
把上面的需求描述转换一下,其实就是要让我们的代理类不再依附于被代理对象。
举例:
写一个日志代理类LogProxyFactory,它可以给任何类代理,在它们的运行前后添加日志,这样就实现了我们的目的:
不需要为被代理类重复写代理类。
说白了,如下图所示的位置,我们要做解耦。
代码
注:带$符号的都是代理类
public interface Movable {
void move();
}
public class PeopleMovable implements Movable {
// 人物移动是我们的主体行为
// 理论上来讲,不需要嵌套到外部
// 不过为了设计合理,也给它嵌套到外部的能力
private Movable m;
public PeopleMovable(Movable m) {
this.m = m;
}
public PeopleMovable() {
}
@Override
public void move() {
if (m != null) {
m.move();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("人物在移动");
}
}
public class ProxyFactory {
// 甚至我们可以直接写ProxyFactory
// 而不做具体的日志类型 ,这样我们的代理工厂,可以代理任意类,也可以做任何形式的代理
private Logger logger = LoggerFactory.getLogger("ProxyFactory");
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
public Object getLogTimeProxyInstance() {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long start = System.currentTimeMillis();
logger.info(this.getClass().getName() + "日志----------》开始");
Object invoke = method.invoke(target, args);
logger.info(this.getClass().getName() + "日志----------》结束");
long end = System.currentTimeMillis();
logger.info("耗时{}毫秒", end - start);
return invoke;
}
});
}
public Object getTimeProxyInstance() {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long start = System.currentTimeMillis();
Object invoke = method.invoke(target, args);
long end = System.currentTimeMillis();
logger.info("耗时{}毫秒", end - start);
return invoke;
}
});
}
public Object getLogProxyInstance() {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
logger.info(this.getClass().getName() + "日志----------》开始");
Object invoke = method.invoke(target, args);
logger.info(this.getClass().getName() + "日志----------》结束");
return invoke;
}
});
}
}
public class MainProxyFactory {
public static void main(String[] args) {
// 串行的
ProxyFactory proxyFactory = new ProxyFactory(new PeopleMovable());
Movable logProxyInstance = (Movable) proxyFactory.getLogProxyInstance();
logProxyInstance.move();
System.out.println("------------------------->");
Movable timeProxyInstance = (Movable) proxyFactory.getTimeProxyInstance();
timeProxyInstance.move();
System.out.println("------------------------->");
// 再来个先日志后耗时的
ProxyFactory proxyFactory1 = new ProxyFactory(timeProxyInstance);
Movable logProxyInstance1 = (Movable) proxyFactory1.getLogProxyInstance();
logProxyInstance1.move();
System.out.println("------------------------->");
// 也可以单独写个,这样调用方便了
Movable logTimeProxyInstance = (Movable) proxyFactory.getLogTimeProxyInstance();
logTimeProxyInstance.move();
// 结果:
// 09:44:52.800 [main] INFO ProxyFactory - proxypattern.ProxyFactory$3日志----------》开始
//人物在移动
//09:44:53.812 [main] INFO ProxyFactory - proxypattern.ProxyFactory$3日志----------》结束
//------------------------->
//人物在移动
//09:44:54.823 [main] INFO ProxyFactory - 耗时1006毫秒
//------------------------->
//09:44:54.833 [main] INFO ProxyFactory - proxypattern.ProxyFactory$3日志----------》开始
//人物在移动
//09:44:55.839 [main] INFO ProxyFactory - 耗时1006毫秒
//09:44:55.840 [main] INFO ProxyFactory - proxypattern.ProxyFactory$3日志----------》结束
//------------------------->
//09:44:55.847 [main] INFO ProxyFactory - proxypattern.ProxyFactory$1日志----------》开始
//人物在移动
//09:44:56.853 [main] INFO ProxyFactory - proxypattern.ProxyFactory$1日志----------》结束
//09:44:56.854 [main] INFO ProxyFactory - 耗时1007毫秒
}
}
总结
如上,把任意类的实例对象传入代理工厂,都可以实现代理,目前有日志和耗时,其他的也都可以继续往代理工厂补充。
这样实现了代理类与被代理类的解耦,一次编码处处代理^^
总而言之,代理模式中的动态代理算是设计模式中比较难理解的
主要是利用了java中的
java.lang.reflect.Proxy
这个类
所以也称为JDK动态代理
含义就是这种代理是利用了JDK的API实现的