“代理模式是对象的结构模式。代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。”
代理模式,简而言之,就是创建一个中间的代理对象,其中包含一个真实对象,用户直接操作代理对象。和上一章提到的装饰者
模式类似,都是需要实现同一个接口。
在代理模式中的角色:
1、抽象对象角色:声明了目标对象和代理对象的共同接口,这样一来在任何可以使用目标对象的地方都可以使用代理对象。
2、目标对象角色:定义了代理对象所代表的目标对象。
3、代理对象角色:代理对象内部含有目标对象的引用,从而可以在任何时候操作目标对象;代理对象提供一个与目标对象相同的接
口,以便可以在任何时候替代目标对象。代理对象通常在客户端调用传递给目标对象之前或之后,执行某个操作,而不是单纯地将
调用传递给目标对象。
公共接口:
public interface Subject {
public void method();
}
真实对象:
public class RealSubject implements Subject {
@Override
public void method() {
// TODO Auto-generated method stub
System.out.println("This is the method in RealSubject.");
}
}
代理对象:
public class ProxySubject implements Subject {
private RealSubject rs;
public ProxySubject(){
rs = new RealSubject();
}
@Override
public void method() {
// TODO Auto-generated method stub
System.out.println("In the proxy before real method...");
rs.method();
System.out.println("In the proxy after real method...");
}
}
测试类:
ublic class TestProxy {
@Test
public void testMethod() {
Subject subject = new ProxySubject();
subject.method();
}
}
这里必须要提一下装饰者模式和代理模式的区别:
对装饰器模式来说,装饰者(decorator)和被装饰者(decoratee)都实现同一个 接口。对代理模式来说,代理类(proxy
class)和真实处理的类(real class)都实现同一个接口。此外,不论我们使用哪一个模式,都可以很容易地在真实对象的方法前面
或者后面加上自定义的方法。
然而,实际上,在装饰器模式和代理模式之间还是有很多差别的。装饰器模式关注于在一个对象上动态的添加方法,然而代理
模式关注于控制对对象的访问。换句话说,用代理模式,代理类(proxy class)可以对它的客户隐藏一个对象的具体信息。因此,
当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。并且,当我们使用装饰器模式的时候,我们通常的做法是
将原始对象作为一个参数传给装饰者的构造器。
上面举到的代理模式的例子都是静态代理,
优点:业务类只需要关注业务逻辑本身,保证了业务类的重用性。这是代理的共有优点。
缺点:
1)代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序
规模稍大时就无法胜任了。
2)如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
在我的其他博文中,介绍了动态代理,在这里就顺便再详细的介绍一下动态代理。
标准接口:
public interface Standard {
public void addUser();
public void delUser();
}
真实类:
public class UserManager implements Standard {
@Override
public void addUser() {
// TODO Auto-generated method stub
System.out.println("This is the origin method to add.");
}
@Override
public void delUser() {
// TODO Auto-generated method stub
System.out.println("This is the origin method to del.");
}
}
创建handler类,实现InvocationHandler接口:
public class LogHandler implements InvocationHandler {
private Object target;
public Object getProxyInstance(Object target) {
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
System.out.println("Log " + method.getName() + " executed.");
//由于真实类中的方法并没有返回值,所以这里没有考虑返回值
//如果方法有返回值,则Object ret = null;然后ret = method.invoke(target,args);
method.invoke(target, args);
System.out.println("Log " + method.getName() + " finished.");
return null;
}
}
测试类:
public class TestLogHandler {
@Test
public void test() {
LogHandler logHandler = new LogHandler();
Standard sd = (Standard) logHandler.getProxyInstance(new UserManager());
sd.delUser();
System.out.println(sd.getClass());
System.out.println(new UserManager().getClass());
}
}
最后的输出为:
Log delUser executed.
This is the origin method to del.
Log delUser finished.
class $Proxy4
class cn.wqy.dynamicproxy.UserManager
优点:
动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理
(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个
方法进行中转。在本示例中看不出来,因为invoke方法体内嵌入了具体的外围业务(记录任务处理前后时间并计算时间差),
实际中可以类似Spring AOP那样配置外围业务。
缺点:
它始终无法摆脱仅支持 interface 代理的桎梏,因为它的设计注定了这个遗憾。回想一下那些动态生成的代理类的继承关系图,
它们已经注定有一个共同的父类叫 Proxy。Java 的继承机制注定了这些动态代理类们无法实现对 class 的动态代理,原因是多继
承在 Java 中本质上就行不通。