代理模式
为某个对象提供一个代理,从而控制这个代理的访问。代理类和委托类具有共同的父类或父接口。在任何使用委托类对象的地方都可以使用代理类对象替代。代理类负责请求的预处理、过滤等,将请求分配给委托类处理、以及委托类处理完请求的后续处理。
代理模式角色:
-
Subject 抽象主题角色
抽象主题类可以是抽象类也可以是接口,是一个最普通的业务类型定义,无特殊要求。 -
RealSubject 具体主题角色
也叫做被委托角色、被代理角色。它才是冤大头,是业务逻辑的具体执行者。 -
Proxy 代理主题角色
也叫做代理类。它负责对真实角色的应用,把所有抽象主题类定义的方法限制委托给真实主题角色实现,并且在真实主题角色处理完毕前后做预处理和善后处理工作。
静态代理
程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
优点:
- 业务类只需要关注业务逻辑本身
缺点:
- 代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,就需要对每个方法都进行代理
- 如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度
实现
1.抽象角色
/**
* IDEA 软件
*/
public interface IDEA {
/**
* 打开 IDEA 的方法
*/
void openIDEA();
}
2.真实角色(.exe文件)
/**
* 真实打开IDEA的可执行文件
*/
@Slf4j
public class ExeFile implements IDEA{
@Override
public void openIDEA() {
log.info("IDEA被打开");
}
}
3.代理角色(快捷方式)
/**
* 代理用来打开IDEA的快捷方式
*/
@Slf4j
public class Shortcut implements IDEA{
private IDEA idea;
public Shortcut(IDEA idea) {
this.idea = idea;
}
@Override
public void openIDEA() {
log.info("快捷方式被双击!!!");
idea.openIDEA();
doOtherThing();
}
public void doOtherThing() {
log.info("我还可以做一些其他事情");
}
}
4.测试类及运行结果
/**
* 静态代理测试类
*/
public class Test {
public static void main(String[] args) {
ExeFile exeFile = new ExeFile();
Shortcut proxy = new Shortcut(exeFile);
proxy.openIDEA();
}
}
------------------运行结果-------------------
15:12:59.204 [main] INFO 快捷方式被双击!!!
15:12:59.209 [main] INFO IDEA被打开
15:12:59.209 [main] INFO 我还可以做一些其他事情
动态代理
程序在运行期间由JVM根据反射等机制动态生成的,所以不存在代理类的字节码文件。代理角色和真实角色的联系在程序运行时确定。
意图:横切面编程,在不改变我们已有代码结构的情况下增强或控制对象的行为
优点:
- 业务类只需要关注业务逻辑本身
- 接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理,接口方法数量比较多的时候,也可以进行灵活的处理,不需要每一个方法都加一个中转方法进行代理
动态代理相关的类及API
Proxy类: Java 动态代理机制生成的所有动态代理类的父类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象。
静态方法 | 作用 |
---|---|
InvocationHandler getInvocationHandler(Object proxy) | 获取指定代理对象所关联的调用处理器 |
Class getProxyClass(ClassLoader loader, Class[] interfaces) | 获取关联于指定类装载器和一组接口的动态代理类的类对象 |
boolean isProxyClass(Class cl) | 判断指定类对象是否是一个动态代理类 |
Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) | 为指定类装载器、一组接口及调用处理器生成动态代理类实例 |
InvocationHandler类: 调用处理器接口,它自定义了一个 invoke 方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。每次生成动态代理类对象时都要指定一个对应的调用处理器对象。
方法 | 作用 |
---|---|
Object invoke(Object proxy, Method method, Object[] args) | 负责集中处理动态代理类上的所有方法调用。三个参数分别是代理类实例对象、被调用方法对象、方法参数 |
ClassLoader 类加载器:
Proxy 静态方法生成的动态代理类与普通类的唯一区别就是其字节码(.class文件)是由 JVM 在运行时动态生成的。
每次生成动态代理类对象时都需要指定一个类加载器对象。
实现
- 实现 InvocationHandler 接口创建自己的调用处理器
- 给 Proxy 类提供 ClassLoader 和代理接口类型数组创建动态代理类
- 以调用处理器类型为参数,利用反射机制得到动态代理类的构造函数
- 以调用处理器对象为参数,利用动态代理类的构造函数创建动态代理类对象
抽象角色
/**
* 抽象角色
*/
public interface AbstractSubject {
void doSomething();
}
真实角色
/**
* 真实角色
*/
@Slf4j
public class RealSubject implements AbstractSubject{
@Override
public void doSomething() {
log.info("任务处理中......");
}
}
调用处理器(相当于代理角色)
@Slf4j
public class SubjectHandler implements InvocationHandler {
AbstractSubject real;
public SubjectHandler(AbstractSubject real){
this.real=real;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log.info("代理类准备开始处理任务");
method.invoke(real ,args);
log.info("代理类处理任务完成");
return null;
}
}
测试类及执行结果
public class Test {
public static void main(String[] args) {
RealSubject real=new RealSubject();
SubjectHandler handler=new SubjectHandler(real);
///生成代理类对象
AbstractSubject proxy = (AbstractSubject) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
new Class[]{AbstractSubject.class},handler);
proxy.doSomething();
}
}
-------------------------运行结果--------------------------
15:33:51.114 [main] INFO 代理类准备开始处理任务
15:33:51.122 [main] INFO 任务处理中......
15:33:51.122 [main] INFO 代理类处理任务完成