代理模式

71 阅读6分钟

代理模式

代理:是指代理人被代理人(又称本人)的名义,在代理权限内与第三人(又称相对人)实施民事行为。

在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。

代理模式通过引入一个代理对象来控制对原对象的访问。代理对象在客户端和目标对象之间充当中介,负责将客户端的请求转发给目标对象,同时可以在转发请求前后进行额外的处理。


结构

  1. 抽象主题类:通过接口或抽象类声明真实主题和代理对象【代理类的实例】的方法。
  2. 真实主题类:实现抽象主题类中的具体业务,是代理对象【代理类的实例】所代表的对象类,
  3. 代理类:实现了真实主题类同样的接口,内部有对真实主题类的引用,可以访问,控制,增强真实主题类。

生活例子

  • 快捷方式:Windows系统中的快捷方式作为文件或程序的代理。
  • 角色扮演:孙悟空作为高翠兰的代理,猪八戒无法区分。
  • 代售点:购买火车票时,代售点作为火车站的代理。
  • 支票:作为银行账户资金的代理,控制资金的访问。
  • Spring AOP:使用代理模式来实现面向切面编程。

火车站卖票例子

火车站把票通过代收点卖票,用户不是自己直接到火车站买票。

静态代理

package wnan.proxy.staticstate;

/*****UTF-8*****
 * Description: 卖票功能接口
 * Author: wnan
 * Create Date: 2024/9/28 21:50
 * Proverbs: 吃得苦中苦,方为人上人
 */
public interface SellTickets {
    Double sell(Integer num);
}

package wnan.proxy.staticstate;

/*****UTF-8*****
 * Description: 火车站,被代理的对象
 * Author: wnan
 * Create Date: 2024/9/28 21:52
 * Proverbs: 吃得苦中苦,方为人上人
 */
public class TrainStation implements SellTickets {
    @Override
    public Double sell(Integer num) {
        return 0.5d * num;
    }
}
package wnan.proxy.staticstate;

/*****UTF-8*****
 * Description: 代理点,代理对象
 * Author: wnan
 * Create Date: 2024/9/28 22:01
 * Proverbs: 吃得苦中苦,方为人上人
 */
public class ProxyPoint implements SellTickets{
    // 火车站
    private TrainStation trainStation = new TrainStation();
    @Override
    public Double sell(Integer num) {
        System.out.println("收取了代理点服务费");
        return trainStation.sell(num);
    }
}
package wnan.proxy.staticstate;

/*****UTF-8*****
 * Description: 静态代理 火车站卖票
 * Author: wnan
 * Create Date: 2024/9/27 20:04
 * Proverbs: 吃得苦中苦,方为人上人
 */
public class Client {
    public static void main(String[] args) {
        // 代理点
        ProxyPoint proxyPoint = new ProxyPoint();
        Double totalAmont = proxyPoint.sell(10);
        System.out.println("车票总价:"+totalAmont);
    }
}

JDK动态代理

动态代理,是程序运行时生成代码,运行,编译时没有代码

jdk动态代理有限制,就是必需要有被代理类的接口

package wnan.proxy.jdk;

/*****UTF-8*****
 * Description: 卖票功能接口 抽象主题类
 * Author: wnan
 * Create Date: 2024/9/28 22:06
 * Proverbs: 吃得苦中苦,方为人上人
 */
public interface SellTickets {
    Double sell(Integer num);
}

package wnan.proxy.jdk;

/*****UTF-8*****
 * Description: 火车站,被代理对象 真实主题类
 * Author: wnan
 * Create Date: 2024/9/28 22:06
 * Proverbs: 吃得苦中苦,方为人上人
 */
public class TrainStation implements SellTickets {
    @Override
    public Double sell(Integer num) {
        return num * 0.5d;
    }
}
package wnan.proxy.jdk;

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

/*****UTF-8*****
 * Description: 代理类
 * Author: wnan
 * Create Date: 2024/9/28 22:08
 * Proverbs: 吃得苦中苦,方为人上人
 */
public class ProxyFactory {
    private TrainStation trainStation = new TrainStation();

    public SellTickets getProxyObject() {
        Object o = Proxy.newProxyInstance(
            trainStation.getClass().getClassLoader(),
            trainStation.getClass().getInterfaces(),
            new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    System.out.println("ProxyFactory getProxyObject invocke-->>>收取代理费");
                    Object invoke = method.invoke(trainStation, args);
                    return invoke;
                }
            }
        );
        return (SellTickets) o;
    }
}

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {}

  • Object proxy:这是代理对象,代表你请求的代理类实例。例如,当你调用一个方法时,实际上是通过这个代理对象来间接调用目标对象的方法。在这样的情况下,proxy 用于表示该代理对象本身。
  • Method method:这个参数表示被调用的方法的元信息。它包含了在目标对象(本例中为 TrainStation)名、返回类型、参数类型等。当代理对象接收到请求时,它会利用这个 method 对象来反射调用目标对象的方法。
  • Object[] args:这是一个对象数组,包含了传递给目标方法的所有参数。当调用目标方法时,方法没有参数,则该数组为 null 或长度为 0。这个参数允许我们在代理方法中灵活地处理不同的方法调用。

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h)

  • Proxy.newProxyInstance()会返回一个代理对象
  • ClassLoader loader: 这个参数指定了定义代理类的类加载器。动态代理类是运行时生成的,它需要通过类加载器来加载。
  • Class<?>[] interfaces: 这个参数是一个类对象数组,表示代理类所实现的接口。代理可以实现多个接口。
  • InvocationHandler h: 这是一个接口,用于处理方法的调用。当代理对象的方法被调用时,实际的操作将交由 InvocationHandler的 invoke方法来完成。
package wnan.proxy.jdk;

/*****UTF-8*****
 * Description: jdk动态代理
 * Author: wnan
 * Create Date: 2024/9/28 22:05
 * Proverbs: 吃得苦中苦,方为人上人
 */
public class Client {
    public static void main(String[] args) {
        ProxyFactory proxyFactory = new ProxyFactory();
        SellTickets proxyObject = proxyFactory.getProxyObject();
        Double totalAmont = proxyObject.sell(10);
        System.out.println(totalAmont);
    }
}

CGLIB动态代理

jdk动态代理有限制,就是必需要有被代理类的接口,这个就是解决这个问题的

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

代码

package wnan.proxy.cglib;

/*****UTF-8*****
 * Description: 火车站,被代理的对象
 * Author: wnan
 * Create Date: 2024/9/28 21:52
 * Proverbs: 吃得苦中苦,方为人上人
 */
public class TrainStation  {
    public Double sell(Integer num) {
        return 0.5d * num;
    }
}
package wnan.proxy.cglib;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/*****UTF-8*****
 * Description: 代理点,代理对象
 * Author: wnan
 * Create Date: 2024/9/28 22:01
 * Proverbs: 吃得苦中苦,方为人上人
 */

public class ProxyPoint implements MethodInterceptor {
    private TrainStation trainStation = new TrainStation();

    @Override
    public TrainStation intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("ProxyPoint implements MethodInterceptor::intercept ");
        // Enhancer这个设置了父类
        Object o1 = methodProxy.invokeSuper(o, args);
        return (TrainStation) o1;
    }

    public TrainStation getProxyObject() {
        // 创建Enhancer对象,类似于jdk动态代理里的Proxy类,
        Enhancer enhancer = new Enhancer();
        // 设置父类字节码对象 手动设置父类
        enhancer.setSuperclass(trainStation.getClass());
        // 设置回调函数
        enhancer.setCallback(this);
        //创建代理对象
        Object o = enhancer.create();
        return (TrainStation) o;
    }
}

Object o:表示被代理的对象,即调用这个方法的实际对象。

Method method:表示被调的方法对象,可以用它获取关于方法的信息(如方法名称、参数类型等)。

Object[] objects:表示调用方法时传入的参数,可以对参数进行处理。

MethodProxy methodProxy:这是CGLIB库中提供的一个类,用于调用被代理对象的方法。invokeSuper 是 MethodProxy

类中的一个方法,用于调用父类(即被代理的对象)中的原始方法。它的作用是直接执行原始方法而不经过代理。

Object intercept(Object var1, Method var2, Object[] var3, MethodProxy var4) throws Throwable; 
//MethodInterceptor接口里的方法,Object被换成了TrainStation,参数也换了,也可以,好神奇
package wnan.proxy.cglib;

/*****UTF-8*****
 * Description: CGLIb动态代理
 * Author: wnan
 * Create Date: 2024/9/29 23:48
 * Proverbs: 吃得苦中苦,方为人上人
 */
public class Client {
    public static void main(String[] args) {
        ProxyPoint proxyPoint = new ProxyPoint();
        TrainStation proxyObject = proxyPoint.getProxyObject();
        Double totalAmont = proxyObject.sell(20);
        System.out.println(totalAmont);
    }
}

不知道为啥,这个错误,我的jdk是17,配置了参数:

--add-opens java.base/java.lang=ALL-UNNAMED

但是不知道为什么,先这样吧 错误:Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @3d71d552

源码 design_pattern