一、什么是代理模式
代理模式的定义:由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
代理模式的主要角色如下:
- 抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
- 真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
- 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。
二、代理模式的实现
根据代理的创建时期,代理模式分为静态代理和动态代理。
- 静态代理:由程序员创建代理类或特定工具自动生成源代码再对其编译,在程序运行前代理类的 .class 文件就已经存在了。
- 动态代理:在程序运行时,运用反射机制动态创建而成,可通过InvocationHandler或者cglib实现。
1、静态代理
- 抽象主题(Subject)类:
java
/**
* @author FluffyCatkin
* @version 1.0
* @date 2020/1/2 0002 18:28
* @description (静态代理)抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
*/
public interface Subject {
void doSomething();
}
- 真实主题(Real Subject)类:
java
/**
* @author FluffyCatkin
* @version 1.0
* @date 2020/1/2 0002 18:29
* @description (静态代理)真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
*/
public class RealSubject implements Subject {
@Override
public void doSomething() {
System.out.println("处理一些事情..............");
}
}
- 代理(Proxy)类:
java
/**
* @author FluffyCatkin
* @version 1.0
* @date 2020/1/2 0002 18:30
* @description (静态代理)代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能
*/
public class Proxy implements Subject {
private Subject subject;
public Proxy(Subject subject){
this.subject = subject;
}
@Override
public void doSomething() {
if (subject==null){
subject = new RealSubject();
}
beforeDoSomething();
subject.doSomething();
afterDoSomething();
}
private void afterDoSomething() {
System.out.println("afterDoSomething............");
}
private void beforeDoSomething() {
System.out.println("beforeDoSomething...............");
}
}
2、通过InvocationHandler实现动态代理
- 动态代理(InvocationHandler)抽象主题(Subject)类:
java
/**
* @author FluffyCatkin
* @version 1.0
* @date 2020/1/3 0003 9:56
* @description (动态代理)抽象主题(Subject)类:通过接口或抽象类来声明真实主题和代理对象实现的业务方法
*/
public interface DynamicSubject {
void doSomething();
}
- 动态代理(InvocationHandler)真实主题(real subject)类:
java
/**
* @author FluffyCatkin
* @version 1.0
* @date 2020/1/3 0003 9:59
* @description (动态代理)真实主题(real subject)类:实现了抽象主题的具体业务实现,是代理对象所代理的真实对象,是最终要引用的对象。
*/
public class DynamicRealSubject implements DynamicSubject {
@Override
public void doSomething() {
System.out.println("在这里处理一些业务.................");
}
}
- 动态代理(InvocationHandler)代理(Proxy)类:
java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author FluffyCatkin
* @version 1.0
* @date 2020/1/3 0003 10:04
* @description (动态代理)代理(Proxy)类:对真实主题功能的扩展
*/
public class DynamicProxy implements InvocationHandler {
//需要代理的对象
private DynamicSubject dynamicSubject;
/**
* 构造方法
* @param dynamicSubject 要代理的对象
*/
public DynamicProxy(DynamicSubject dynamicSubject){
this.dynamicSubject = dynamicSubject;
}
/**
*
* @param proxy 被代理的类
* @param method 要增强的方法
* @param args 增强的方法参数
* @return 增强的方法返回值
* @throws Throwable 异常
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开始增强..................");
System.out.println("增强的方法是..."+method.getName());
if (args!=null&&args.length>0){
for (Object arg : args) {
System.out.println(arg.toString());
}
}
Object invoke = method.invoke(dynamicSubject, args);
System.out.println("增强结束..................");
return invoke;
}
/**
* 获取被代理的对象
* @return 被代理的对象
*/
public DynamicSubject getDynamicSubject() {
return dynamicSubject;
}
/**
* 获取代理后增强的对象
* @param dynamicSubject 被代理的对象
* @return 代理后增强的对象
*/
public static DynamicSubject newInstance(DynamicSubject dynamicSubject) {
InvocationHandler invocationHandler = new DynamicProxy(dynamicSubject);
return (DynamicSubject) Proxy.newProxyInstance(DynamicSubject.class.getClassLoader(),
new Class[]{DynamicSubject.class},invocationHandler);
}
}
3、通过cglib实现动态代理
- (cglib)抽象主题(Subject)类:
java
/**
* @author FluffyCatkin
* @version 1.0
* @date 2020/1/3 0003 11:50
* @description (cglib)抽象主题(Subject)类:通过接口或抽象类来声明真实主题和代理对象实现的业务方法
*/
public interface CglibSubject {
void doSomething();
}
- (cglib)真实主题(real subject)类:
java
/**
* @author FluffyCatkin
* @version 1.0
* @date 2020/1/3 0003 11:52
* @description (cglib)真实主题(real subject)类:实现了抽象主题的具体业务实现,是代理对象所代理的真实对象,是最终要引用的对象。
*/
public class CglibRealSubject implements CglibSubject {
@Override
public void doSomething() {
System.out.println("处理自己的业务....................");
}
}
- cglib)代理(Proxy)类:
java
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @author FluffyCatkin
* @version 1.0
* @date 2020/1/3 0003 11:53
* @description (cglib)代理(Proxy)类:对真实主题功能的扩展
*/
public class CglibProxy implements MethodInterceptor {
private CglibSubject cglibSubject;
public CglibProxy(CglibSubject cglibSubject) {
this.cglibSubject = cglibSubject;
}
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("增强....................");
Object invoke = methodProxy.invokeSuper(o, objects);
System.out.println("增强结束..................");
return invoke;
}
public static CglibSubject newInstance(CglibSubject cglibSubject){
CglibProxy cglibProxy = new CglibProxy(cglibSubject);
Enhancer enhancer = new Enhancer();//帮我们生成代理对象
enhancer.setSuperclass(CglibRealSubject.class);//设置对谁进行代理
enhancer.setCallback(cglibProxy);//代理要做什么
return (CglibSubject) enhancer.create();//创建代理对象
}
}
4、测试类
java
/**
* @author FluffyCatkin
* @version 1.0
* @date 2020/1/2 0002 17:55
* @description 代理模式
*
*在有些情况下,一个客户不能或者不想直接访问另一个对象,这时需要找一个中介帮忙完成某项任务,这个中介就是代理对象。例如,购买火车票不一定要去火车站买,可以通过 12306 网站或者去火车票代售点买。又如找女朋友、找保姆、找工作等都可以通过找中介完成。
*
* 在软件设计中,使用代理模式的例子也很多,例如,要访问的远程对象比较大(如视频或大图像等),其下载要花很多时间。还有因为安全原因需要屏蔽客户端直接访问真实对象,如某单位的内部数据库等。
* 代理模式的定义与特点:
* 代理模式的定义:由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
*
* 代理模式的主要优点有:代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
* 代理对象可以扩展目标对象的功能;
* 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度;
*
* 其主要缺点是:在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
* 增加了系统的复杂度;
* 代理模式的结构与实现:
* 代理模式的结构比较简单,主要是通过定义一个继承抽象主题的代理来包含真实主题,从而实现对真实主题的访问,下面来分析其基本结构和实现方法。
* 模式的结构:
* 代理模式的主要角色如下。抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
* 真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
* 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。
*
* 静态代理 动态代理 CGLIB代理
*
*/
public class Main {
/**
* 测试静态代理
*/
@Test
public void staticProxyTest(){
Subject subject = new RealSubject();
Proxy proxy = new Proxy(subject);
proxy.doSomething();
}
/**
* 测试动态代理
*/
@Test
public void dynamicTest(){
DynamicSubject dynamicSubject = DynamicProxy.newInstance(new DynamicRealSubject());
dynamicSubject.doSomething();
}
/**
* cglib代理测试
*/
@Test
public void cglibTest(){
CglibSubject cglibSubject = CglibProxy.newInstance(new CglibRealSubject());
cglibSubject.doSomething();
}
}
运行结果:
- 测试静态代理:
java
beforeDoSomething...............
处理一些事情..............
afterDoSomething............
Process finished with exit code 0
- 测试InvocationHandler动态代理:
java
开始增强..................
增强的方法是...doSomething
在这里处理一些业务.................
增强结束..................
Process finished with exit code 0
- 测试cglib动态代理:
java
增强....................
处理自己的业务....................
增强结束..................
Process finished with exit code 0
三、应用场景
当无法或不想直接引用某个对象或访问某个对象存在困难时,可以通过代理对象来间接访问。使用代理模式主要有两个目的:一是保护目标对象,二是增强目标对象。
一般代理模式有以下的应用场景:
- 远程代理,这种方式通常是为了隐藏目标对象存在于不同地址空间的事实,方便客户端访问。例如,用户申请某些网盘空间时,会在用户的文件系统中建立一个虚拟的硬盘,用户访问虚拟硬盘时实际访问的是网盘空间。
- 虚拟代理,这种方式通常用于要创建的目标对象开销很大时。例如,下载一幅很大的图像需要很长时间,因某种计算比较复杂而短时间无法完成,这时可以先用小比例的虚拟代理替换真实的对象,消除用户对服务器慢的感觉。
- 安全代理,这种方式通常用于控制不同种类客户对真实对象的访问权限。
智能指引,主要用于调用目标对象时,代理附加一些额外的处理功能。例如,增加计算真实对象的引用次数的功能,这样当该对象没有被引用时,就可以自动释放它。 - 延迟加载,指为了提高系统的性能,延迟对目标的加载。例如,Hibernate 中就存在属性的延迟加载和关联表的延时加载。
四、优缺点分析
代理模式的主要优点有:
- 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
- 代理对象可以扩展目标对象的功能;
- 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度,增加了程序的可扩展性
其主要缺点是:
- 代理模式会造成系统设计中类的数量增加
- 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
- 增加了系统的复杂度;