代理模式
代理:是指代理人以被代理人(又称本人)的名义,在代理权限内与第三人(又称相对人)实施民事行为。
在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。
代理模式通过引入一个代理对象来控制对原对象的访问。代理对象在客户端和目标对象之间充当中介,负责将客户端的请求转发给目标对象,同时可以在转发请求前后进行额外的处理。
结构
- 抽象主题类:通过接口或抽象类声明真实主题和代理对象【代理类的实例】的方法。
- 真实主题类:实现抽象主题类中的具体业务,是代理对象【代理类的实例】所代表的对象类,
- 代理类:实现了真实主题类同样的接口,内部有对真实主题类的引用,可以访问,控制,增强真实主题类。
生活例子
- 快捷方式: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