目录
代理模式是常用的结构型设计模式之一,当无法直接访问某个对象或访问某个对象存在困难时可以通过一个代理对象来间接访问,为了保证客户端使用的透明性,所访问的真实对象与代理对象需要实现相同的接口。
根据代理模式的使用目的不同,代理模式又可以分为多种类型,例如保护代理、远程代理、虚拟代理、缓冲代理等,它们应用于不同的场合,满足用户的不同需求。
代理模式概述
代理模式是一种应用很广泛的结构型设计模式。
代理模式定义如下:
代理模式(Proxy):为其他对象提供一种代理以控制对这个对象的访问。
代理模式是一种对象结构型模式。在代理模式中引入了一个新的代理对象,代理对象在客户端对象和目标对象之间起到中介的作用,它去掉客户不能看到的内容和服务或者增添客户需要的额外的新服务。
所谓的代理者是指一个类别可以作为其它东西的接口。代理者可以作任何东西的接口:网上连接、存储器中的大对象、文件或其它昂贵或无法复制的资源。
代理模式结构与实现
模式结构
代理模式的结构比较简单,其核心是代理类,为了让客户端能够一致性地对待真实对象和代理对象,在代理模式中引入了抽象层,代理模式结构如下图所示:
代理模式包含如下三个角色:
- Subject:它声明了真实主题和代理主题的共同接口,这样一来在任何使用真实主题的地方都可以使用代理主题,客户端通常需要针对抽象主题角色进行编程。
- Proxy:它包含了对真实主题的引用,从而可以在任何时候操作真实主题对象;在代理主题角色中提供一个与真实主题角色相同的接口,以便在任何时候都可以替代真实主题;代理主题角色还可以控制对真实主题的使用,负责在需要的时候创建和删除真实主题对象,并对真实主题对象的使用加以约束。通常,在代理主题角色中,客户端在调用所引用的真实主题操作之前或之后还需要执行其他操作,而不仅仅是单纯调用真实主题对象中的操作。
- RealSubject:它定义了代理角色所代表的真实对象,在真实主题角色中实现了真实的业务操作,客户端可以通过代理主题角色间接调用真实主题角色中定义的操作。
在实际开发过程中,代理类的实现比上述代码要复杂很多,代理模式根据其目的和实现方式不同可分为很多种类,其中常用的几种代理模式简要说明如下:
- 远程代理(Remote Proxy):为一个位于不同的地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以是在同一台主机中,也可是在另一台主机中,远程代理又称为大使(Ambassador)。
- 虚拟代理(Virtual Proxy):如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。
- 保护代理(Protect Proxy):控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限。
- 缓冲代理(Cache Proxy):为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。
- 智能引用代理(Smart Reference Proxy):当一个对象被引用时,提供一些额外的操作,例如将对象被调用的次数记录下来等。
主要分为静态代理和动态代理两类,动态代理分为JDK动态代理和cglib代理
案例分析
静态代理
subject类
public interface HelloService {
public void hello();
}
RealSubject类:
public class HelloServiceImpl implements HelloService {
@Override
public void hello() {
System.out.println("hello ");
}
}
Proxy类
public class HelloServiceProxy implements HelloService {
HelloService helloService;
public HelloServiceProxy(HelloService service){
this.helloService=service;
}
@Override
public void hello() {
System.out.println("before ");
helloService.hello();
System.out.println("end");
}
}
客户端调用
public class Client {
public static void main(String[] args) {
HelloService helloService=new HelloServiceImpl();
HelloServiceProxy helloServiceProxy=new HelloServiceProxy(helloService);
helloServiceProxy.hello();
}
}
优点:可以做到在符合开闭原则的情况下对目标对象进行功能扩展。
缺点:我们得为每一个服务都得创建代理类,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改。
JDK动态代理
proxy:
public class DynamicalProxy {
private HelloService helloService;
public DynamicalProxy(HelloService service){
this.helloService=service;
}
public Object getProxy(){
return Proxy.newProxyInstance(helloService.getClass().getClassLoader(), helloService.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
Object o=method.invoke(helloService,args);
System.out.println("end");
return o;
}
});
}
}
注意Proxy.newProxyInstance()方法接受三个参数:
ClassLoader loader
:指定当前目标对象使用的类加载器,获取加载器的方法是固定的Class<?>[] interfaces
:指定目标对象实现的接口的类型,使用泛型方式确认类型InvocationHandler:
指定
动态处理器,
执行目标对象的方法时,会触发事件处理器的方法
客户端调用
public class Client {
public static void main(String[] args) {
HelloService helloService=new HelloServiceImpl();
HelloService dynamicalProxy=(HelloService) new DynamicalProxy(helloService).getProxy();
dynamicalProxy.hello();
}
}
动态代理总结:虽然相对于静态代理,动态代理大大减少了我们的开发任务,同时减少了对业务接口的依赖,降低了耦合度。但是还是有一点点小小的遗憾之处,那就是它始终无法摆脱仅支持interface代理的桎梏。
Cglib代理
JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢,这就需要CGLib了。CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。但因为采用的是继承,所以不能对final修饰的类进行代理。JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。
需要引入两个jar包:cglib.jar,asm.jar
要增强的类
public class UserDao {
public void save(){
System.out.println("hello");
}
}
代理类
public class ProxyFactory implements MethodInterceptor {
//维护目标对象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
//给目标对象创建代理对象
public Object getProxyInstance(){
//工具类
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(target.getClass());
//设置回调方法
enhancer.setCallback(this);
//创建子类对象
return enhancer.create();
}
//执行父类方法的时候调用的拦截器,在此添加增强父类方法的行为
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("before");
Object obj = methodProxy.invokeSuper(o,args);
System.out.println("end");
return obj;
}
}
客户端调用
public class Client {
public static void main(String[] args) {
UserDao target = new UserDao();
UserDao proxyFactory = (UserDao) new ProxyFactory(target).getProxyInstance();
proxyFactory.save();
}
}