1、代理模式的学习
代理模式是一种结构型设计模式,它允许你提供一个代理对象,该对象可以控制对其他对象的访问。代理模式通过在代理对象和实际对象之间添加一个中间层,使得代理对象可以代表实际对象执行某些操作,从而实现对实际对象的间接访问。
代理模式的主要目的是在不改变原始对象的情况下,为其提供额外的功能、控制或保护。它可以在不直接访问实际对象的情况下,管理对象的创建、销毁、访问控制等。
代理模式涉及以下几个角色:
- 抽象主题(Subject):定义了代理对象和实际对象的共同接口,这样代理对象就可以通过实现该接口来代表实际对象。
- 真实主题(Real Subject):实际对象,它定义了代理对象所代表的真实对象。代理对象通过调用实际对象的方法来提供额外的功能或控制访问。
- 代理(Proxy):代理对象,它包含一个对实际对象的引用,并实现了抽象主题定义的接口。代理对象可以在执行实际对象的操作前后进行一些预处理和后处理,以实现附加的功能。
代理模式可以分为以下几种类型:
- 静态代理:在编译时就已经确定代理对象和实际对象的关系,代理对象和实际对象是一一对应的关系。代理对象需要显式地实现或继承抽象主题接口,并调用实际对象的方法。
- 动态代理:在运行时动态生成代理对象,无需显式地实现或继承抽象主题接口。动态代理通常使用 Java 提供的 Proxy 类或第三方库(如CGLIB)来实现。通过动态代理,可以根据需要代理不同的实际对象,并在运行时添加额外的行为。
2、代理模式的使用
题目:假设你正在开发一个音乐播放器应用程序,你需要实现一个日志记录器,用于记录每次播放音乐的歌曲名称。使用代理模式来实现一个日志记录器,确保每次播放音乐时都能自动记录日志信息。
2.1、静态代理模式
/**
* @author myf
* 抽象主题
*/
public interface MusicPlayer {
/**
* 播放
*
* @param songName
*/
void play(String songName);
}
/**
* @Author: myf
* @CreateTime: 2023-06-01 16:36
* @Description: AppleMusicPlayer 真实主题 苹果音乐播放器
*/
public class AppleMusicPlayer implements MusicPlayer {
@Override
public void play(String songName) {
System.out.println("苹果音乐播放器开始播放" + songName + "音乐");
}
}
/**
* @Author: myf
* @CreateTime: 2023-06-01 16:37
* @Description: AppleMusicPlayerProxy 代理 苹果音乐播放器代理类
*/
public class AppleMusicPlayerProxy implements MusicPlayer {
private static final Logger LOGGER = LoggerFactory.getLogger(AppleMusicPlayerProxy.class);
private AppleMusicPlayer appleMusicPlayer;
public AppleMusicPlayerProxy(AppleMusicPlayer appleMusicPlayer) {
this.appleMusicPlayer = appleMusicPlayer;
}
@Override
public void play(String songName) {
LOGGER.info("日志记录:苹果音乐播放器即将开始播放");
appleMusicPlayer.play(songName);
LOGGER.info("日志记录:苹果音乐播放器结束播放");
}
}
结果
public class ProxyClient {
public static void main(String[] args) {
AppleMusicPlayerProxy appleMusicPlayerProxy =
new AppleMusicPlayerProxy(new AppleMusicPlayer());
appleMusicPlayerProxy.play("七里香");
}
}
21:17:06.907 [main] INFO org.myf.designPattern.design.proxy.staticPattern.AppleMusicPlayerProxy - 日志记录:苹果音乐播放器即将开始播放
苹果音乐播放器开始播放七里香音乐
21:17:06.912 [main] INFO org.myf.designPattern.design.proxy.staticPattern.AppleMusicPlayerProxy - 日志记录:苹果音乐播放器结束播放
2.2、jdk动态代理
JDK 动态代理是在运行时期生成代理类的字节码。动态代理基于接口实现,通过反射和 InvocationHandler 接口来实现。在运行时,通过调用 Proxy.newProxyInstance 方法创建代理对象,传入被代理接口和调用处理器对象。当代理对象的方法被调用时,实际执行的是调用处理器的 invoke 方法,通过反射调用被代理对象的相应方法,并在前后添加额外的逻辑。
/**
* @Author: myf
* @CreateTime: 2023-06-01 16:40
* @Description: AppleMusicPlayerJdkProxy 基于jdk的苹果音乐播放器代理类
*/
public class AppleMusicPlayerJdkProxy implements InvocationHandler {
private static final Logger LOGGER =
LoggerFactory.getLogger(AppleMusicPlayerJdkProxy.class);
//传入被代理的对象也就是实际对象
private Object proxy;
//构造方法引入实际对象
public AppleMusicPlayerJdkProxy(Object proxy) {
this.proxy = proxy;
}
/**
*
* @param proxy jdk动态代理生成的代理对象
* @param method 我们所要调用的某个方法
* @param args 所要调用的方法的入参
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
LOGGER.info("日志记录:苹果音乐播放器即将开始播放");
//传入实际对象,去执行实际对象的method,args是方法入参
method.invoke(this.proxy, args);
LOGGER.info("日志记录:苹果音乐播放器结束播放");
return null;
}
}
public class ProxyClient {
public static void main(String[] args) {
AppleMusicPlayerJdkProxy appleMusicPlayerJdkProxy = new
AppleMusicPlayerJdkProxy(new AppleMusicPlayer());
//ClassLoader对象,定义了由哪个类加载器来对生成的代理对象进行加载,
//Interface对象的数组,表示的是动态代理对象所要实现的接口
//InvocationHandler对象,表示的是动态代理对象在调用方法的时候,会关联到哪一个
//InvocationHandler对象上。
MusicPlayer musicPlayer = (MusicPlayer)
Proxy.newProxyInstance(MusicPlayer.class.getClassLoader(),
new Class[]{MusicPlayer.class}, appleMusicPlayerJdkProxy);
musicPlayer.play("暗香");
}
}
21:21:28.449 [main] INFO org.myf.designPattern.design.proxy.jdkPattern.AppleMusicPlayerJdkProxy - 日志记录:苹果音乐播放器即将开始播放
苹果音乐播放器开始播放暗香音乐
21:21:28.451 [main] INFO org.myf.designPattern.design.proxy.jdkPattern.AppleMusicPlayerJdkProxy - 日志记录:苹果音乐播放器结束播放
2.3、cglib动态代理
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>2.2</version>
</dependency>
/**
* @Author: myf
* @CreateTime: 2023-06-01 17:17
* @Description: AppleMusicPlayerCglibProxy 基于cglib的苹果音乐播放器代理类
*/
public class AppleMusicPlayerCglibProxy implements MethodInterceptor {
private static final Logger LOGGER =
LoggerFactory.getLogger(AppleMusicPlayerCglibProxy.class);
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class clazz) {
//设置代理类的父类,即被代理的类
enhancer.setSuperclass(clazz);
//设置代理类的回调对象,即拦截器。
enhancer.setCallback(this);
//创建代理对象
return enhancer.create();
}
/**
* @param obj 代理对象即生成的代理类的实例
* @param method 被拦截的目标方法。
* @param args 目标方法的参数数组。
* @param proxy MethodProxy 对象,用于调用原始方法。
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
LOGGER.info("日志记录:苹果音乐播放器即将开始播放");
Object result = proxy.invokeSuper(obj, args);
LOGGER.info("日志记录:苹果音乐播放器结束播放");
return result;
}
}
public class ProxyClient {
public static void main(String[] args) {
AppleMusicPlayerCglibProxy appleMusicPlayerCglibProxy = new
AppleMusicPlayerCglibProxy();
AppleMusicPlayer cglibAppleMusicPlayer = (AppleMusicPlayer)
appleMusicPlayerCglibProxy.getProxy(AppleMusicPlayer.class);
cglibAppleMusicPlayer.play("花香");
}
}
21:39:22.930 [main] INFO org.myf.designPattern.design.proxy.cglibPattern.AppleMusicPlayerCglibProxy - 日志记录:苹果音乐播放器即将开始播放
苹果音乐播放器开始播放花香音乐
21:39:22.943 [main] INFO org.myf.designPattern.design.proxy.cglibPattern.AppleMusicPlayerCglibProxy - 日志记录:苹果音乐播放器结束播放
3、总结
- jdk动态代理
JDK 动态代理基于接口实现。它使用 java.lang.reflect.Proxy 类和java.lang.reflect.InvocationHandler 接口来创建代理对象。
首先,定义一个实现 InvocationHandler 接口的代理类,它负责处理代理对象的方法调用。
然后,使用 Proxy.newProxyInstance(ClassLoader, Class[], InvocationHandler) 方法创建代理对象,指定类加载器、要代理的接口列表和代理类的实例。
当调用代理对象的方法时,实际上会调用 InvocationHandler 实现类的 invoke 方法,通过反射调用被代理对象的方法,并可以在方法调用前后执行额外的逻辑。 - CGLIB
CGLIB(Code Generation Library)是一个强大的高性能代码生成库,它能够在运行时生成子类来代理目标类,而不需要接口。
CGLIB 基于继承实现代理(不能对final修饰的类进行代理),它使用 net.sf.cglib.proxy.Enhancer 类和 net.sf.cglib.proxy.MethodInterceptor 接口来创建代理对象。首先,定义一个实现 MethodInterceptor 接口的代理类,它负责处理代理对象的方法调用。
然后,创建 Enhancer 对象,并设置被代理类为其父类,设置 MethodInterceptor 实现类为回调对象。
最后,通过调用 Enhancer 对象的 create 方法来创建代理对象。
当调用代理对象的方法时,CGLIB 会拦截方法调用,通过调用 MethodInterceptor 实现类的 intercept 方法,并可以在方法调用前后执行额外的逻辑。在 intercept 方法中,使用 MethodProxy 对象调用父类的方法,从而实现代理。