一、什么是代理模式
代理模式为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用, 其特征是代理类与委托类有同样的接口。代理模式是常用的java设计模式。
代理模式能够在不修改源码的情况下增强方法,在方法前后增加日志记录,权限管理等功能。
表现形式如图:
img
在Java中,代理模式分成2种:静态代理模式和动态代理模式;
-
静态代理的模式在平时生活中也很常见,比如买火车票这件小事,黄牛相当于是火车站的代理,我们可以通过黄牛或者代售点进行买票行为,但只能去火车站进行改签和退票,因为只有火车站才有改签和退票的方法。
-
动态代理中,代理类并不是在Java代码中实现,而是在运行时期生成,相比静态代理,动态代理可以很方便的对委托类的方法进行统一处理,如添加方法调用次数、添加日志功能等等,动态代理分为jdk动态代理和cglib动态代理,下面通过一个例子看看如何实现jdk动态代理。
二、代理模式的优缺点
-
优点:
-
职责清晰。
-
高扩展性。
-
智能化。
-
-
缺点:
-
由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
-
实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
-
-
其他模式对比
-
和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。
-
和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。
-
三、使用场景
代理模式主要解决在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
1.应用场景
按职责来划分,通常有以下使用场景:
-
远程代理。
-
虚拟代理。
-
Copy-on-Write 代理。
-
保护(Protect or Access)代理。
-
Cache 代理。
-
防火墙(Firewall)代理。
-
同步化(Synchronization)代理。
-
智能引用(Smart Reference)代理。
2.应用实例
-
Windows 桌面以及资源管理器里面的快捷方式。
-
买火车票不一定在火车站买,也可以去代售点,也可以网购。
-
Spring AOP 中 用到的动态代理。
四、代码实现
1. 静态代理模式
静态代理满足如下的几个条件:
-
代理对象的类是真实存在的,并非动态生成的。
-
代理对象持有被代理对象的引用。
-
代理对象中的方法通过被代理对象的引用调用被代理对象的方法,同时执行代理逻辑。
下面是一个简单的代理模式:
接口类:
/*
* 接口
* 代理类和委托类都必须实现该类
*/
public interface ISubject {
//处理任务的抽象方法
public void dealTask(String task);
}
委托类:
/*
* 真实角色(被代理类,委托类)
* 实现接口Isubject
*/
public class RealSubject implements ISubject {
@Override
public void dealTask(String task) {
//真实角色处理任务
System.out.println("正在"+task);
}
}
代理类:
/*
* 代理类
* 实现ISubject接口
* 持有被代理类的引用
*/
public class Proxy implements ISubject {
//持有被代理类的引用
private RealSubject realSubject;
//在构造方法中初始化被代理类
public Proxy(RealSubject r) {
this.realSubject=r;
}
@Override
//在该方法中加入代理逻辑
public void dealTask(String task) {
System.out.println("开始帮忙....");
//加入线程睡眠模拟帮忙的行为
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//真正执行动作的仍然是真实角色
realSubject.dealTask(task);
}
}
静态工厂类:
/*
* 静态工厂类
* 对于客户端来说 并不关心执行动作的是代理对象还是真实角色
* 所以我们创建一个静态方法直接返回对象
*/
public class StaticFactory {
//调用此方法获得实例
public static ISubject getInstance(){
return new Proxy(new RealSubject());
}
}
测试类:
/*
* 测试类(客户类)
*/
public class Test {
public static void main(String[] args) {
//创建代理对象时需要一个真实对象的实例
ISubject instance = StaticFactory.getInstance();
//使用代理类的方法
instance.dealTask("敲代码");
}
}
真正的业务功能还是有委托类来实现,但是在实现业务类之前的一些公共服务。例如在项目开发中我们没有加入缓冲,日志这些功能,后期想加入,我们就可以使用代理来实现,而没有必要打开已经封装好的委托类。
2.动态代理模式
1、定义业务逻辑
public interface Service {
//目标方法
public abstract void add();
}
public class UserServiceImpl implements Service {
public void add() {
System.out.println("This is add service");
}
}
2、利用 java.lang.reflect.Proxy
类和 java.lang.reflect.InvocationHandler
接口定义代理类的实现。
class MyInvocatioHandler implements InvocationHandler {
private Object target;
public MyInvocatioHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("-----before-----");
Object result = method.invoke(target, args);
System.out.println("-----end-----");
return result;
}
// 生成代理对象
public Object getProxy() {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class<?>[] interfaces = target.getClass().getInterfaces();
return Proxy.newProxyInstance(loader, interfaces, this);
}
}
3、使用动态代理
public class ProxyTest {
public static void main(String[] args) {
Service service = new UserServiceImpl();
MyInvocatioHandler handler = new MyInvocatioHandler(service);
Service serviceProxy = (Service)handler.getProxy();
serviceProxy.add();
}
}
执行结果:
-----before-----
This is add service
-----end-----