代理模式就是在不修改源码的情况下,对其进行功能扩展。比如在使用某方法前进行日志记录,对用户进行权限控制,预处理消息、过滤消息等等。
需要知道的是,代理类本身不实现服务,而是通过调用被代理类中的方法来提供服务。
在Java中主要分为静态代理和动态代理模式,动态代理是一种比较常用的代理模式,因为它比静态代理更加的灵活。但是我们也是有必要了解静态代理。
静态静态,顾名思义就是死的,专业术语来说就是已编译的,在程序运行前就已经存在代理类的字节码文件。
实现静态代理的步骤:
比如我想要个代理帮我刷副本,并且在代理上线的时候输出一下日志。首先刷副本能力在于我本身,代理只是上我的号。
接口
package 静态代理;
// 定义玩家接口
public interface Player {
// 定义刷副本函数
void DaGuai();
}
被代理类
package 静态代理;
public class PlayerImpl implements Player {
@Override
public void DaGuai() {
System.out.println("上下上下左右左右BABA");
}
}
代理类
package 静态代理;
public class StaticProxy implements Player {
// 定义被代理类的对象
private Player player;
public StaticProxy(Player player){
this.player = player;
}
@Override
public void DaGuai() {
// 输出进度
System.out.println(player + "准备刷副本了");
// 被代理类的方法
player.DaGuai();
System.out.println(player + "刷完副本了");
}
}
main函数
package 静态代理;
public class Main {
public static void main(String[] args){
// 创建玩家对象
Player codekiang = new PlayerImpl();
// 将玩家对象封装到代理对象中
StaticProxy proxy = new StaticProxy(codekiang);
proxy.DaGuai();
}
}
到此就完成了静态代理的实现。我们可以在DaGuai
方法里扩展自己想要的功能。但是静态代理这么写只能代理一个方法或一个类。
如果一个类中有多个方法需要代理呢,比如我不仅要刷副本,我还想刷完副本代理帮我刷元宝,那代理跟被代理对象就需要再重写一个刷元宝的方法。
再比如,我需要再来一个类,该类的功能是播放音乐,我需要有代理类帮我播放,并输出日志。此时就需要再重新写一个代理类来播放音乐了。
可以见得,静态代理面对这种情况很棘手。我们可不可以只编写接口并实现它该有的功能,然后只创建一个代理对象帮我们代理这些方法呢?
动态代理拍了拍你的屁股并说:嘿,Boy。
动态代理类 使用了反射,使其能在程序运行时创建代理类的代理模式。动态代理可以实现AOP编程、解耦。
实现动态代理的步骤:
IncocationHandler
接口的invoke
方法)流程图:
代理器会自动帮我们创建代理对象,动态代理对象所有的方法在调用时都会被拦截,送到代理处理器的invoke()
方法来处理。
实现代码:
接口(Player跟Listen)
package 动态代理;
public interface Player {
void play();
}
package 动态代理;
public interface Listen {
void listen();
}
被代理类(实现了两个接口)
package 动态代理;
public class PlayerImpl implements Player, Listen {
@Override
public void play() {
System.out.println("刷副本中。。。");
}
@Override
public void listen() {
System.out.println("正在播放《搁浅》。。。");
}
}
动态代理类:
package 动态代理;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DynamicProxy implements InvocationHandler {
private Player player;
private Listen listen;
public DynamicProxy(Player player){
this.player = player;
}
public DynamicProxy(Listen listen){
this.listen = listen;
}
@Override
/*
* Object proxy:被代理的对象
* Method method:要调用的方法
* Object[] args:方法调用时所需要参数
* */
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("动态代理的Class对象:"+proxy.getClass());
System.out.println("动态代理的Class对象的类名:"+proxy.getClass().getName());
System.out.println("动态代理的Class对象调用的方法:"+method.getName());
Object result = null;
switch (method.getName()){
case "listen":
System.out.println(listen + "准备听音乐了");
result = method.invoke(listen, args);
System.out.println(listen + "听完了。。。");
break;
case "play":
System.out.println(player + "准备刷副本了");
result = method.invoke(player, args);
System.out.println(player + "刷完副本了");
break;
default:break;
}
return result;
}
}
客户端(main函数)
package 动态代理;
import java.lang.reflect.Proxy;
public class Main {
public static void main(String[] args){
// 创建对象
Player codekiangPlayer = new PlayerImpl();
// 创建调用处理器对象
DynamicProxy handlerPlayer = new DynamicProxy(codekiangPlayer);
// 动态生成代理对象
Player proxyPlayer = (Player) Proxy.newProxyInstance(
PlayerImpl.class.getClassLoader(), // 获取对象的类加载器
PlayerImpl.class.getInterfaces(), // 获取对象所有的接口
handlerPlayer // 调用处理器对象
);
// 调用被代理类的方法
proxyPlayer.play();
System.out.println("============分割线============");
// 创建对象
Listen codekiangListen = new PlayerImpl();
// 创建调用处理器对象
DynamicProxy ListenHandler = new DynamicProxy(codekiangListen);
// 动态生成代理对象
Listen ListenProxy = (Listen) Proxy.newProxyInstance(
PlayerImpl.class.getClassLoader(),
PlayerImpl.class.getInterfaces(),
ListenHandler
);
ListenProxy.listen();
}
}
需要注意的是,当实现多个接口时,实现的顺序很重要,当多个接口有同名方法时,代理对象会执行位于前面的接口的方法。