Guava源码分析——Proxy模式(TimeLimiter)
代理模式:给某一对象提供代理对象,并由代理对象控制具体对象的引用。主要解决的问题是:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
interface Image {
public void displayImage();
}
class RealImage implements Image {
private String filename;
public RealImage(String filename) {
this.filename = filename;
loadImageFromDisk();
}
private void loadImageFromDisk() {
System.out.println("Loading " + filename);
}
public void displayImage() {
System.out.println("Displaying " + filename);
}
}
class ProxyImage implements Image {
private String filename;
private Image image;
public ProxyImage(String filename) {
this.filename = filename;
}
public void displayImage() {
if(image == null)
image = new RealImage(filename);
image.displayImage();
}
}
class ProxyExample {
public static void main(String[] args) {
Image image = new ProxyImage("MyPhoto");
image.displayImage();
}
}
运行结果:
与装饰模式的区别:
装饰模式关注于在一个对象上动态的添加方法,然而代理模式关注于控制对对象的访问。换句话 说,用代理模式,代理类可以对它的客户隐藏一个对象的具体信息。因此,当使用代理模式的时候,我们通常在一个代理类中创建一个对象的实例。当我们使用装饰器模式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器。使用代理模式,代理和真实对象之间的的关系通常在编译时就已经确定了,而装饰者能够在运行时递归地被构造,例如:
//蓝莓冰淇淋
AbstractIceCream blueberryIceCream = new BlueberryAdapter(new IceCream());
//蓝莓巧克力冰淇淋
AbstractIceCream bb_ch_iceCream = new BlueberryAdapter(new ChocolateAdapter(new IceCream()));
//加3层巧克力
AbstractIceCream lot_of_chocolate_iceCream = new ChocolateAdapter(new ChocolateAdapter(new ChocolateAdapter(new IceCream())));
在Guava的TimeLimiter类中就应用到了代理模式:
TimeLimiter:用于对某一类对象的所有方法运行时间进行限制,即在给定时限内方法正常执行,否则抛出异常。该类生成一个代理,当调用被代理的对象的方法时,会强制加一个时间限制。定义了 newProxy(T target, Class<T> interfaceType, long timeoutDuration, TimeUnit timeoutUnit) 和 callWithTimeOut(Callable<T> callable, long timeoutDuration, TimeUnit timeoutUnit, boolean interruptible)。
1、 newProxy()方法生成 target的一个代理
注意:参数interfaceType必须为接口类型。
在该方法中,首先会找出接口的interruptible方法:
Set<Method> set = Sets.newHashSet(); for (Method m : interfaceType.getMethods()) { if (declaresInterruptedEx(m)) { set.add(m); } }这里会判断接口的每一个方法是否抛出InterruptedException异常,如果没有抛出,则不会加到interruptible方法集合里。
例如上面得到的Method集合为:
在调用callWithTimeout时, 该集合用来判断,调用的方法是否amInterruptible。
以判断应该调用future.get(timeoutDuration, timeoutUnit); 或Uninterruptibles.getUninterruptibly(future, timeoutDuration, timeoutUnit);来获取执行结果。
下面例子中,将直接使用上面代理模式中定义的类:
TimeLimiter limiter = new SimpleTimeLimiter(); Image image = new ProxyImage("MyPhoto"); Image proxy = limiter.newProxy(image, Image.class, 1000, TimeUnit.MILLISECONDS); try { proxy.displayImage(); } catch (Exception e) { e.printStackTrace(); }运行结果:
2、 callWithTimeout()方法:如果 callable执行完毕时还没有到达限定时间则将结果或异常传给调用者,否则抛出 UncheckedTimeoutException。用法如下:
public class TimeLimiterTest { public static void main(String[] args){ SimpleTimeLimiter simpleTimeLimiter = new SimpleTimeLimiter(); String hello = null; try { hello = simpleTimeLimiter.callWithTimeout(new Callable<String>(){ @Override public String call() throws Exception { return "Hello"; }}, 100, TimeUnit.MILLISECONDS, true); } catch (Exception e) { e.printStackTrace(); } System.out.println(hello); String word = null; try { word = simpleTimeLimiter.callWithTimeout(new Callable<String>(){ @Override public String call() throws Exception { Thread.sleep(100); return "Word"; }}, 99, TimeUnit.MILLISECONDS, true); } catch (Exception e) { e.printStackTrace(); } System.out.println(word); }}结果:运行超时