温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Java结构型模式之代理模式怎么实现

发布时间:2023-02-20 09:47:38 来源:亿速云 阅读:132 作者:iii 栏目:开发技术

Java结构型模式之代理模式怎么实现

1. 代理模式概述

1.1 什么是代理模式

代理模式(Proxy Pattern)是一种结构型设计模式,它允许通过创建一个代理对象来控制对另一个对象的访问。代理对象作为客户端和目标对象之间的中介,可以在不改变目标对象的情况下,增加额外的功能或控制访问。

1.2 代理模式的应用场景

代理模式在以下场景中非常有用:

  • 远程代理:为一个对象在不同的地址空间提供局部代表。例如,远程方法调用(RMI)中的存根(Stub)对象。
  • 虚拟代理:根据需要创建开销很大的对象。例如,延迟加载图片或大型文档。
  • 保护代理:控制对原始对象的访问权限。例如,权限控制。
  • 智能引用:在访问对象时执行额外的操作。例如,引用计数、懒加载、日志记录等。

1.3 代理模式的优缺点

优点:

  • 职责清晰:代理对象可以处理与目标对象无关的逻辑,使得目标对象更加专注于核心业务。
  • 扩展性好:通过代理对象,可以在不修改目标对象的情况下增加功能。
  • 安全性高:代理对象可以控制对目标对象的访问,增加安全性。

缺点:

  • 增加复杂性:引入代理对象会增加系统的复杂性。
  • 性能开销:代理对象的引入可能会增加系统的性能开销,尤其是在频繁调用的情况下。

2. 代理模式的实现方式

代理模式可以通过多种方式实现,主要包括静态代理和动态代理。下面我们将详细介绍这两种实现方式。

2.1 静态代理

静态代理是指在编译时就已经确定了代理类和目标类的关系。代理类和目标类都实现了相同的接口,代理类在调用目标类的方法前后可以执行额外的操作。

2.1.1 静态代理的实现步骤

  1. 定义接口:定义一个接口,代理类和目标类都实现这个接口。
  2. 实现目标类:目标类实现接口,并定义具体的业务逻辑。
  3. 实现代理类:代理类也实现接口,并在调用目标类方法前后执行额外的操作。
  4. 客户端调用:客户端通过代理类来调用目标类的方法。

2.1.2 静态代理的代码示例

// 1. 定义接口
interface Subject {
    void request();
}

// 2. 实现目标类
class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("RealSubject: Handling request.");
    }
}

// 3. 实现代理类
class ProxySubject implements Subject {
    private RealSubject realSubject;

    public ProxySubject(RealSubject realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public void request() {
        System.out.println("ProxySubject: Before handling request.");
        realSubject.request();
        System.out.println("ProxySubject: After handling request.");
    }
}

// 4. 客户端调用
public class StaticProxyDemo {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        ProxySubject proxySubject = new ProxySubject(realSubject);
        proxySubject.request();
    }
}

输出结果:

ProxySubject: Before handling request.
RealSubject: Handling request.
ProxySubject: After handling request.

2.1.3 静态代理的优缺点

优点:

  • 简单易懂:静态代理的实现方式简单,易于理解。
  • 职责清晰:代理类和目标类的职责清晰,代理类负责额外的逻辑,目标类负责核心业务。

缺点:

  • 代码冗余:如果目标类有多个方法,代理类需要为每个方法都实现代理逻辑,导致代码冗余。
  • 扩展性差:如果目标类发生变化,代理类也需要相应地进行修改,扩展性较差。

2.2 动态代理

动态代理是指在运行时动态生成代理类,而不是在编译时确定。Java提供了两种方式来实现动态代理:基于接口的JDK动态代理和基于类的CGLIB动态代理。

2.2.1 JDK动态代理

JDK动态代理是基于接口的代理方式,它通过java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来实现。

2.2.1.1 JDK动态代理的实现步骤
  1. 定义接口:定义一个接口,目标类实现这个接口。
  2. 实现目标类:目标类实现接口,并定义具体的业务逻辑。
  3. 实现InvocationHandler:实现InvocationHandler接口,在invoke方法中定义代理逻辑。
  4. 创建代理对象:通过Proxy.newProxyInstance方法创建代理对象。
  5. 客户端调用:客户端通过代理对象来调用目标类的方法。
2.2.1.2 JDK动态代理的代码示例
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 1. 定义接口
interface Subject {
    void request();
}

// 2. 实现目标类
class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("RealSubject: Handling request.");
    }
}

// 3. 实现InvocationHandler
class DynamicProxyHandler implements InvocationHandler {
    private Object target;

    public DynamicProxyHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("DynamicProxy: Before handling request.");
        Object result = method.invoke(target, args);
        System.out.println("DynamicProxy: After handling request.");
        return result;
    }
}

// 4. 创建代理对象
public class JDKDynamicProxyDemo {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        Subject proxySubject = (Subject) Proxy.newProxyInstance(
                realSubject.getClass().getClassLoader(),
                realSubject.getClass().getInterfaces(),
                new DynamicProxyHandler(realSubject)
        );
        proxySubject.request();
    }
}

输出结果:

DynamicProxy: Before handling request.
RealSubject: Handling request.
DynamicProxy: After handling request.
2.2.1.3 JDK动态代理的优缺点

优点:

  • 灵活性高:动态代理可以在运行时动态生成代理类,灵活性高。
  • 代码简洁:不需要为每个方法都实现代理逻辑,代码更加简洁。

缺点:

  • 基于接口:JDK动态代理只能基于接口进行代理,如果目标类没有实现接口,则无法使用JDK动态代理。

2.2.2 CGLIB动态代理

CGLIB(Code Generation Library)是一个强大的、高性能的代码生成库,它可以在运行时扩展Java类和实现接口。CGLIB动态代理是基于类的代理方式,它通过继承目标类来生成代理类。

2.2.2.1 CGLIB动态代理的实现步骤
  1. 定义目标类:定义一个目标类,不需要实现接口。
  2. 实现MethodInterceptor:实现MethodInterceptor接口,在intercept方法中定义代理逻辑。
  3. 创建代理对象:通过Enhancer类创建代理对象。
  4. 客户端调用:客户端通过代理对象来调用目标类的方法。
2.2.2.2 CGLIB动态代理的代码示例
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

// 1. 定义目标类
class RealSubject {
    public void request() {
        System.out.println("RealSubject: Handling request.");
    }
}

// 2. 实现MethodInterceptor
class CglibProxyInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("CglibProxy: Before handling request.");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("CglibProxy: After handling request.");
        return result;
    }
}

// 3. 创建代理对象
public class CglibDynamicProxyDemo {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(RealSubject.class);
        enhancer.setCallback(new CglibProxyInterceptor());
        RealSubject proxySubject = (RealSubject) enhancer.create();
        proxySubject.request();
    }
}

输出结果:

CglibProxy: Before handling request.
RealSubject: Handling request.
CglibProxy: After handling request.
2.2.2.3 CGLIB动态代理的优缺点

优点:

  • 基于类:CGLIB动态代理可以基于类进行代理,不需要目标类实现接口。
  • 性能高:CGLIB动态代理的性能通常比JDK动态代理更高。

缺点:

  • 依赖第三方库:CGLIB是一个第三方库,需要额外引入依赖。
  • 无法代理final类和方法:CGLIB无法代理final类和方法,因为它是通过继承目标类来生成代理类的。

3. 代理模式的应用实例

3.1 远程代理

远程代理用于在不同的地址空间中代表对象。例如,远程方法调用(RMI)中的存根(Stub)对象就是远程代理的一种实现。

3.1.1 远程代理的实现步骤

  1. 定义远程接口:定义一个远程接口,客户端和服务器端都实现这个接口。
  2. 实现远程对象:服务器端实现远程接口,并定义具体的业务逻辑。
  3. 创建存根对象:通过RMI机制创建存根对象,客户端通过存根对象调用远程方法。
  4. 客户端调用:客户端通过存根对象调用远程方法。

3.1.2 远程代理的代码示例

import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

// 1. 定义远程接口
interface RemoteSubject extends Remote {
    void request() throws RemoteException;
}

// 2. 实现远程对象
class RemoteSubjectImpl extends UnicastRemoteObject implements RemoteSubject {
    protected RemoteSubjectImpl() throws RemoteException {
        super();
    }

    @Override
    public void request() throws RemoteException {
        System.out.println("RemoteSubject: Handling request.");
    }
}

// 3. 创建存根对象
public class RemoteProxyDemo {
    public static void main(String[] args) {
        try {
            RemoteSubject remoteSubject = new RemoteSubjectImpl();
            // 注册远程对象
            java.rmi.Naming.rebind("//localhost/RemoteSubject", remoteSubject);
            System.out.println("RemoteSubject bound in registry");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

客户端调用:

import java.rmi.Naming;

public class RemoteClient {
    public static void main(String[] args) {
        try {
            RemoteSubject remoteSubject = (RemoteSubject) Naming.lookup("//localhost/RemoteSubject");
            remoteSubject.request();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

输出结果:

RemoteSubject: Handling request.

3.2 虚拟代理

虚拟代理用于根据需要创建开销很大的对象。例如,延迟加载图片或大型文档。

3.2.1 虚拟代理的实现步骤

  1. 定义接口:定义一个接口,代理类和目标类都实现这个接口。
  2. 实现目标类:目标类实现接口,并定义具体的业务逻辑。
  3. 实现代理类:代理类也实现接口,并在调用目标类方法时根据需要创建目标对象。
  4. 客户端调用:客户端通过代理类来调用目标类的方法。

3.2.2 虚拟代理的代码示例

// 1. 定义接口
interface Image {
    void display();
}

// 2. 实现目标类
class RealImage implements Image {
    private String filename;

    public RealImage(String filename) {
        this.filename = filename;
        loadFromDisk();
    }

    private void loadFromDisk() {
        System.out.println("Loading image: " + filename);
    }

    @Override
    public void display() {
        System.out.println("Displaying image: " + filename);
    }
}

// 3. 实现代理类
class ProxyImage implements Image {
    private String filename;
    private RealImage realImage;

    public ProxyImage(String filename) {
        this.filename = filename;
    }

    @Override
    public void display() {
        if (realImage == null) {
            realImage = new RealImage(filename);
        }
        realImage.display();
    }
}

// 4. 客户端调用
public class VirtualProxyDemo {
    public static void main(String[] args) {
        Image image = new ProxyImage("test.jpg");
        // 图片未加载
        image.display();
        // 图片已加载
        image.display();
    }
}

输出结果:

Loading image: test.jpg
Displaying image: test.jpg
Displaying image: test.jpg

3.3 保护代理

保护代理用于控制对原始对象的访问权限。例如,权限控制。

3.3.1 保护代理的实现步骤

  1. 定义接口:定义一个接口,代理类和目标类都实现这个接口。
  2. 实现目标类:目标类实现接口,并定义具体的业务逻辑。
  3. 实现代理类:代理类也实现接口,并在调用目标类方法时检查访问权限。
  4. 客户端调用:客户端通过代理类来调用目标类的方法。

3.3.2 保护代理的代码示例

// 1. 定义接口
interface SensitiveData {
    void access();
}

// 2. 实现目标类
class RealSensitiveData implements SensitiveData {
    @Override
    public void access() {
        System.out.println("Accessing sensitive data.");
    }
}

// 3. 实现代理类
class ProtectionProxy implements SensitiveData {
    private RealSensitiveData realSensitiveData;
    private String userRole;

    public ProtectionProxy(String userRole) {
        this.userRole = userRole;
    }

    @Override
    public void access() {
        if ("admin".equals(userRole)) {
            if (realSensitiveData == null) {
                realSensitiveData = new RealSensitiveData();
            }
            realSensitiveData.access();
        } else {
            System.out.println("Access denied. You do not have the required permissions.");
        }
    }
}

// 4. 客户端调用
public class ProtectionProxyDemo {
    public static void main(String[] args) {
        SensitiveData data = new ProtectionProxy("user");
        data.access(); // Access denied

        data = new ProtectionProxy("admin");
        data.access(); // Accessing sensitive data
    }
}

输出结果:

Access denied. You do not have the required permissions.
Accessing sensitive data.

3.4 智能引用

智能引用用于在访问对象时执行额外的操作。例如,引用计数、懒加载、日志记录等。

3.4.1 智能引用的实现步骤

  1. 定义接口:定义一个接口,代理类和目标类都实现这个接口。
  2. 实现目标类:目标类实现接口,并定义具体的业务逻辑。
  3. 实现代理类:代理类也实现接口,并在调用目标类方法时执行额外的操作。
  4. 客户端调用:客户端通过代理类来调用目标类的方法。

3.4.2 智能引用的代码示例

// 1. 定义接口
interface Resource {
    void use();
}

// 2. 实现目标类
class RealResource implements Resource {
    @Override
    public void use() {
        System.out.println("Using real resource.");
    }
}

// 3. 实现代理类
class SmartReferenceProxy implements Resource {
    private RealResource realResource;
    private int referenceCount = 0;

    @Override
    public void use() {
        if (realResource == null) {
            realResource = new RealResource();
        }
        realResource.use();
        referenceCount++;
        System.out.println("Reference count: " + referenceCount);
    }
}

// 4. 客户端调用
public class SmartReferenceProxyDemo {
    public static void main(String[] args) {
        Resource resource = new SmartReferenceProxy();
        resource.use();
        resource.use();
    }
}

输出结果:

Using real resource.
Reference count: 1
Using real resource.
Reference count: 2

4. 代理模式的扩展

4.1 代理模式的变体

代理模式有多种变体,常见的有以下几种:

  • 虚拟代理:延迟创建开销大的对象。
  • 远程代理:为远程对象提供本地代表。
  • 保护代理:控制对原始对象的访问权限。
  • 智能引用:在访问对象时执行额外的操作。

4.2 代理模式与其他模式的结合

代理模式可以与其他设计模式结合使用,以实现更复杂的功能。例如:

  • 代理模式与装饰器模式:装饰器模式用于动态地给对象添加职责,而代理模式用于控制对对象的访问。两者可以结合使用,以实现更灵活的代理功能。
  • 代理模式与观察者模式:观察者模式用于定义对象间的一对多依赖关系,当对象状态发生变化时,所有依赖它的对象都会收到通知。代理模式可以与观察者模式结合使用,以实现对目标对象的监控和通知。

5. 总结

代理模式是一种非常有用的结构型设计模式,它通过引入代理对象来控制对目标对象的访问。代理模式有多种实现方式,包括静态代理和动态代理(JDK动态代理和CGLIB动态代理)。代理模式在远程代理、虚拟代理、保护代理和智能引用等场景中都有广泛的应用。

通过代理模式,我们可以在不修改目标对象的情况下,增加额外的功能或控制访问。代理模式的灵活性和扩展性使得它在实际开发中非常有用。然而,代理模式也带来了一定的复杂性和性能开销,因此在使用时需要权衡利弊。

希望本文能够帮助你更好地理解和应用代理模式,在实际开发中灵活运用代理模式来解决各种问题。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI