设计模式(十三) 代理模式和Java动态代理

来源:互联网 发布:mysql读写分离原理 编辑:程序博客网 时间:2024/05/21 17:53

Proxy Pattern

1. 什么是代理模式

代理模式:给某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问。
Proxy Pattern: Provide a surrogate or placeholder for another object to control access to it.
代理模式是一种对象结构型模式。

在代理模式中引入了一个新的代理对象,代理对象在客户端对象和目标对象之间起到中介的作用,它去掉客户不能看到的内容和服务或者增添客户需要的额外的新服务。

主要解决的问题是:在直接访问对象时带来的问题。

2. 代理模式类角色解析

代理模式结构图:
代理模式

为了让客户端能够一致性地对待真实对象和代理对象,在代理模式中引入了抽象层。

  • Subject(抽象主题角色):它声明了真实主题和代理主题的共同接口,这样一来在任何使用真实主题的地方都可以使用代理主题,客户端通常需要针对抽象主题角色进行编程。
  • Proxy(代理主题角色):它包含了对真实主题的引用,从而可以在任何时候操作真实主题对象;在代理主题角色中提供一个与真实主题角色相同的接口,以便在任何时候都可以替代真实主题;代理主题角色还可以控制对真实主题的使用,负责在需要的时候创建和删除真实主题对象,并对真实主题对象的使用加以约束。
    通常,在代理主题角色中,客户端在调用所引用的真实主题操作之前或之后还需要执行其他操作,而不仅仅是单纯调用真实主题对象中的操作。
  • RealSubject(真实主题角色):它定义了代理角色所代表的真实对象,在真实主题角色中实现了真实的业务操作,客户端可以通过代理主题角色间接调用真实主题角色中定义的操作。

3. 代理模式和装饰模式区别

代理模式和装饰模式的最大区别就是目的: 代理模式控制的是对象的访问控制,而装饰模式仅仅是装饰一下,增加一些功能。

在代码上,代理模式内部持有真实对象的引用,而装饰模式通过构造方法等外部传入一个对象。

4. 动态代理

代理的实现分为:

  1. 静态代理:代理类是在编译时就实现好的。也就是说 Java 编译完成后代理类是一个实际的 class 文件。
  2. 动态代理:代理类是在运行时生成的。也就是说 Java 编译完之后并没有实际的 class 文件,而是在运行时动态生成的类字节码,并加载到JVM中。

动态代理是指在运行时动态生成代理类。即,代理类的字节码将在运行时生成并载入当前代理的 ClassLoader。与静态处理类相比,动态类有诸多好处。

  • 首先,不需要为Subject(抽象主题角色)写一个形式上完全一样的封装类,如果主题接口中的方法很多,为每一个接口写一个代理方法也很麻烦。
  • 如果接口有变动,则真实主题和代理类都要修改,不利于系统维护;
  • 使用一些动态代理的生成方法甚至可以在运行时制定代理类的执行逻辑,从而大大提升系统的灵活性。

更多请参考:
代理模式原理及实例讲解
代理模式及Java实现动态代理

5. 代理模式好处

  • 代理模式能够协调调用者和被调用者,在一定程度上降低了系 统的耦合度。
  • 客户端可以针对抽象主题角色进行编程,增加和更换代理类无须修改源代码,符合开闭原则,系统具有较好的灵活性和可扩展性。
  • 使用动态代理更加解耦

6. 一个很好的代理模式应用例子

图片代理:一个很常见的代理模式的应用实例就是对大图浏览的控制。
用户通过浏览器访问网页时先不加载真实的大图,而是通过代理对象的方法来进行处理,在代理对象的方法中,先使用一个线程向客户端浏览器加载一个小图片,然后在后台使用另一个线程来调用大图片的加载方法将大图片加载到客户端。当需要浏览大图片时,再将大图片在新网页中显示。如果用户在浏览大图时加载工作还没有完成,可以再启动一个线程来显示相应的提示信息。通过代理技术结合多线程编程将真实图片的加载放到后台来操作,不影响前台图片的浏览。

7. 代码示例

从以下示例可以看到动态代理确实很方便。

7.1 静态代理示例:

//Subject,抽象主题角色public interface Subject {    void request();}//RealSubject,真实主题角色public class RealSubject implements Subject {    @Override public void request() {        System.out.println("真正的request");    }}//Proxy(代理主题角色)public class ProxySubject implements Subject {    private Subject subject;//内部直接持有真正的Subject对象,对其进行访问控制。    public ProxySubject() {        subject = new RealSubject();    }    @Override public void request() {        System.out.println("before====");        subject.request();        System.out.println("after====");    }}//测试类public class MainClass {    public static void main(String[] args) {        Subject subject  = new ProxySubject();        subject.request();    }}

7.2 动态代理示例:

//Subject,抽象主题角色public interface Subject {    void request();}//RealSubject(真实主题角色)public class RealSubject implements Subject {    @Override public void request() {        System.out.println("真正的request");    }}//ProxyHandler,动态代理Handlerpublic class ProxyHandler implements InvocationHandler {    private Subject subject;//内部直接持有真正的Subject对象,对其进行访问控制。    public ProxyHandler() {        this.subject = new RealSubject();    }    @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        System.out.println("before====");        //调用真实Subject的方法        Object invoke = method.invoke(subject, args);        System.out.println("after====");        return invoke;    }}//测试类public class ZZMainClass {    public static void main(String[] args) {        Subject subject = (Subject) Proxy.newProxyInstance(Subject.class.getClassLoader(), new Class[] {                Subject.class        }, new ProxyHandler());        subject.request();}

打印结果:

before====真正的requestafter====
0 0
原创粉丝点击