设计模式-代理模式

1、概述

代理模式(Proxy Pattern)属于结构型设计模式,其核心思想是为其他对象提供一种代理以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介的作用,并可以在不改变客户端代码的情况下增强或控制对象的访问。

代理模式主要分为三种类型:

静态代理:由程序员创建或第三方工具生成代理类的源代码,再进行编译。 其代理类和委托类在编译期间就确定下来。
动态代理: 代理类在程序运行时通过反射等机制动态生成,无需程序员显式地创建代理类。
JDK 动态代理
CGLib 动态代理
虚拟代理: 当有一个需要创建开销较大的对象时,通过虚拟代理来存放实例化需要较长时间的真实对象。

2、优缺点

优点:

控制访问:代理模式可以控制对对象的访问,可以在访问对象之前或之后执行一些额外的操作。例如:日志记录、功能增强、权限验证、性能监控等。
保护对象:代理模式可以对真实对象进行保护,只允许特定的客户端访问真实对象,从而提供了一定的安全性。
延迟加载:代理模式可以延迟加载真实对象,当需要真实对象时才进行创建和初始化,从而提高了系统的性能和资源利用率。
简化客户端:代理模式隐藏了真实对象的复杂性,客户端可以通过代理对象来完成操作,无需直接与真实对象交互。

缺点:

增加复杂性:因为引入了代理对象,使得对对象的管理和维护更加复杂。
性能损耗:由于代理模式在访问对象时引入了额外的间接层,可能会导致性能上的损耗。每次通过代理访问对象都需要经过额外的处理步骤,可能会对系统的响应时间产生影响。

3、实现方式

3.1、静态代理




//测试类
public class Test {
	
    public static void main(String[] args) {
        // 直接调用
        RealPlayer realPlayer = new RealPlayer();
        realPlayer.playVideo("《西游记》.mp4");
        System.out.println("===========================");

        // 代理方式调用,代理真实对象
        PlayerProxy playerProxy = new PlayerProxy(realPlayer);
        playerProxy.loadVideo("《三国演义》.mp4");
        playerProxy.playVideo("《三国演义》.mp4");

    }

}

public interface Player {
    void loadVideo(String filename);
    void playVideo(String filename);
}

public class RealPlayer implements Player {

    @Override
    public void loadVideo(String filename) {
        System.out.println("加载MP4视频文件: " + filename);
    }

    @Override
    public void playVideo(String filename) {
        System.out.println("播放MP4视频文件: " + filename);
    }

}

public class PlayerProxy implements Player{

    private RealPlayer realPlayer;

    public PlayerProxy(RealPlayer realPlayer){
        this.realPlayer = realPlayer;
    }

    @Override
    public void loadVideo(String filename) {
        realPlayer.loadVideo(filename);
    }

    @Override
    public void playVideo(String filename) {
        realPlayer.playVideo(filename);
    }

}

3.2、JDK动态代理

JDK 动态代理可以在运行时动态生成一个代理对象,是 Java 标准库中提供的一种代理方法,代理对象实现和原始类一样的接口,并将方法调用转发给被代理对象,同时还可以在方法调用前后执行额外的增强处理。JDK 动态代理通过反射机制实现代理功能。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//测试类
public class Test {

    public static void main(String[] args) {
        // 直接调用
        RealPlayer realPlayer = new RealPlayer();
        //realPlayer.playVideo("《西游记》.mp4");
        System.out.println("===========================");

        // 代理方式调用,代理真实对象
        Player proxy = (Player) new JDKProxyFactory(realPlayer).getProxy();
        proxy.loadVideo("《三国演义》.mp4");
        proxy.playVideo("《三国演义》.mp4");

    }

}

public class JDKProxyFactory implements InvocationHandler {
    // 需要被代理的对象
    private Object object;

    public JDKProxyFactory(Object object) {
        this.object = object;
    }

    @SuppressWarnings("unchecked")
    public Object getProxy() {
        return Proxy.newProxyInstance(
        	object.getClass().getClassLoader(), 
        	object.getClass().getInterfaces(),
        	this
        );
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;
        // 进行方法匹配,调用对应方法名的方法
        if ("loadVideo".equals(method.getName())) {
            result = method.invoke(object, args);
        }

        if ("playVideo".equals(method.getName())) {
            System.out.println("前置增强");
            result = method.invoke(object, args);
            System.out.println("后置增强");
        }
        return result;
    }
    
}




public interface Player {
    void loadVideo(String filename);
    void playVideo(String filename);
}

public class RealPlayer implements Player {

    @Override
    public void loadVideo(String filename) {
        System.out.println("加载MP4视频文件: " + filename);
    }

    @Override
    public void playVideo(String filename) {
        System.out.println("播放MP4视频文件: " + filename);
    }

}

public class PlayerProxy implements Player{

    private RealPlayer realPlayer;

    public PlayerProxy(RealPlayer realPlayer){
        this.realPlayer = realPlayer;
    }

    @Override
    public void loadVideo(String filename) {
        realPlayer.loadVideo(filename);
    }

    @Override
    public void playVideo(String filename) {
        realPlayer.playVideo(filename);
    }

}

3.3、CGLib动态代理

需要引入第三方 CGLIB 包

4、应用场景

远程(Remote)代理
本地服务通过网络请求远程服务。为了实现本地到远程的通信,我们需要实现网络通信,处理其中可能的异常。为良好的代码设计和可维护性,我们将网络通信部分隐藏起来,只暴露给本地服务一个接口,通过该接口即可访问远程服务提供的功能,而不必过多关心通信部分的细节。

防火墙(Firewall)代理
当你将浏览器配置成使用代理功能时,防火墙就将你的浏览器的请求转给互联网;当互联网返回响应时,代理服务器再把它转给你的浏览器。

保护(Protect or Access)代理
控制对一个对象的访问,如果需要,可以给不同的用户提供不同级别的使用权限。

This entry was posted in 应用, 设计模式. Bookmark the permalink.