在阎宏博士的《JAVA与模式》一书中开头是这样描述代理(Proxy)模式的:代理模式是对象的结构模式。代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。
在代理模式(Proxy Pattern)中,由于客户端无法直接或者不想直接引用或使用一个对象,所以通过“中间件”起到代理目标对象功能的作用,为其他对象提供一种代理以控制对这个对象的访问。
具体主题类(ConcreteSubject):该类也称为被代理类或被委托类,定义了一个真实的对象,最终执行代理类的代理方法中的业务逻辑,客户端通过代理类间接调用真实主题类中定义的方法;
代理类(ProxySubject):该类也被称为委托类或代理类,该类持有一个对真实主题类的引用,在它的方法中调用与真实主题类对应的接口方法;
静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。
在生活中我们打官司找律师,买房找中介这些场景中都有代理的身影,这里我们以买房子找中介来模拟代理模式。
缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多,同时,一旦接口增加方法,目标对象与代理对象都要.
静态代理是最简单的代理表现形式,它的UML图跟装饰者模式的比较像,但是表达的含义用途是完全不一样的:
装饰者模式的核心是动态的扩展功能,是在Decorator类中持有一个具体组件的引用,然后动态扩展具体组件的功能。
动态代理是指在运行时动态生成代理类。即代理类的字节码将在运行时生成并载入当前代理的 ClassLoader。在JDK中通过Proxy类进行实现,Proxy类提供了静态的方法用于动态创建代理类和实例。代理实例是代理类的一个实例。 每个代理实例都有一个关联的调用处理程序对象,即实现接口 InvocationHandler的自定义实例对象。
ClassLoader loader:指定当前目标对象使用类加载器,获取加载器的方法是固定的
InvocationHandler handler:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入
每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其到它的调用处理程序的 invoke 方法。
动态代理是不需要定义代理角色的,通过一个处理器来处理代理角色的业务逻辑。动态代理有以下特点:
代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
在数据库连接中,我们都是通过系统封装好的Driver类来进行数据库的操作。这里我以此来模拟一下。
代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理。动态就体现在用接口来动态指向上。
通过getProxyClass获取对应的Class对象,然后在通过反射获取对应的实例,绑定处理器完成。
不需要实主题写一个形式上完全一样的代理类,假如主题接口中的方法很多,为每一个接口写一个代理方法也很麻烦。如果接口有变动,则真实主题和代理类都要修改,不利于系统。
使用一些动态代理的生成方法甚至可以在运行时制定代理类的执行逻辑,从而大大提升系统的灵活性。比如过滤数据。
动态代理类使用字节码动态生成加载技术,在运行时生成加载类。生成动态代理类的方法很多,如,JDK 自带的动态处理、CGLIB、Javassist 或者 ASM 库。JDK 的动态代理使用简单,它内置在 JDK 中,因此不需要引入第三方 Jar 包,但相对功能比较弱。CGLIB 和 Javassist 都是高级的字节码生成库,总体性能比 JDK 自带的动态代理好,而且功能十分强大。ASM 是低级的字节码生成工具,使用 ASM 已经近乎于在使用 Java bytecode 编程,对开发人员要求最高,当然,也是性能最好的一种动态代理生成工具。但 ASM 的使用很繁琐,而且性能也没有数量级的提升,与 CGLIB 等高级字节码生成工具相比,ASM 程序的性较差,如果不是在对性能有苛刻要求的场合,还是推荐 CGLIB 或者 Javassist。(引用自【周明耀-代理模式原理及实例】)
为一个对象在不同的地址空间提供局部代表,这样可以隐藏一个对象存在于不同地址空间的事实。使服务器端Server实现对客户端的隐藏,以便Server可以忽略服务器端。
是根据需要创建开销很大的对象,通过它来存放实例化需要很长时间的真实对象,并在需要的时候进行加载。
|