代理模式概述
在有些情况下,一个对象不能或者不想直接访问另一个对象,这时需要找一个中介帮忙完成某项任务,这个中介就是代理对象。例如,购买火车票不一定要去火车站买,可以通过 12306 网站或者去火车票代售点买。又如找女朋友、找保姆、找工作等都可以通过找中介完成。
代理模式的定义:由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
模式的结构
代理模式的主要角色如下:
- 抽象主题:通过接口或抽象类声明被代理对象和代理对象实现的业务方法。
- 真实主题:实现了抽象主题中的具体业务,是代理对象要代理的真实对象,是最终要引用的目标对象。
- 代理类:访问、控制或扩展真实主题的功能。
在代码中,代理一般会被理解为代码增强,实际上就是在原代码逻辑前后增加一些代码逻辑,而使调用者无感知。
根据代理类的创建时期,代理模式分为静态代理和动态代理。
-
静态:由程序员创建代理类或特定工具自动生成源代码再对其编译,在程序运行前代理类的 .class 文件就已经存在了。
-
动态:在程序运行时,运用反射机制动态创建代理类的 .class文件
静态代理
使用静态代理时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类。
代码演示:
//抽象主题
interface AbstractSubject {
void operate();
}
//真实主题
class RealSubject implements AbstractSubject {
@Override
public void operate() {
System.out.println("真实主题的处理方法...");
}
}
//静态代理类
class Proxy implements AbstractSubject {
private RealSubject realSubject;
public Proxy(RealSubject realSubject) {
this.realSubject = realSubject;
}
/**
* 代理类的operate方法,可以在真实对象的operate方法前后增加处理逻辑
*/
@Override
public void operate() {
beforeOperate();
realSubject.operate();
afterOperate();
}
private void beforeOperate() {
System.out.println("operate方法的预处理");
}
private void afterOperate() {
System.out.println("operate方法的后置处理");
}
}
//访问类
public class Client {
public static void main(String[] args) {
Proxy proxy = new Proxy(new RealSubject());
proxy.operate();
}
}
动态代理
JDK动态代理
使用JDK动态代理时,目标对象要实现接口,代理类由 JDK 的API 在程序运行时在内存中动态创建
JDK 中生成代理对象的API:
代理类所在包:java.lang.reflect.Proxy
,生成代理对象的方法:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
方法参数说明:
-
ClassLoader loader:被代理类的类加载器
-
Class<?>[] interfaces:被代理类的实现的所有接口
-
InvocationHandler h:代理实例的调用处理程序实现的接口,每个代理实例都有一个关联的调用处理程序。在代理实例上调用方法时,该方法调用将被编码并分派到其调用处理程序的invoke方法。源码:
package java.lang.reflect; public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
invoke方法参数说明:
- Object proxy:代理实例
- Method method:代理实例调用的方法对应的 Method 实例
- Object[] args:代理实例调用的方法的参数值的对象数组
invoke方法返回值:代理实例的方法调用返回的值,若代理实例的方法无返回值,则返回null
代码演示:
//抽象主题
interface AbstractSubject {
int operate(int a, int b);
}
//真实主题
class RealSubject implements AbstractSubject {
@Override
public int operate(int a, int b) {
System.out.println("真实主题的处理方法...");
return a + b;
}
}
//访问类
public class Client {
public static void main(String[] args) {
//创建目标对象(被代理对象)
RealSubject realSubject = new RealSubject();
//设置保存运行期生成的jdk动态代理类的class文件
//System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
//生成代理对象
AbstractSubject proxy = (AbstractSubject) Proxy.newProxyInstance(
RealSubject.class.getClassLoader(),
RealSubject.class.getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//增加前置处理
beforeOperate();
//调用operate方法
Object obj = method.invoke(realSubject, args);
//遍历operate方法实参
for (int i = 0; i < args.length; i++) {
System.out.println("第" + i + "个参数:" + args[i]);
}
//增加后置处理
afterOperate();
return obj;
}
});
//代理对象调用operate方法,间接访问目标对象
int value = proxy.operate(5, 10);
System.out.println("operate方法的返回值:" + value);
}
//前置处理逻辑
private static void beforeOperate() {
System.out.println("operate方法的预处理");
}
//后置处理逻辑
private static void afterOperate() {
System.out.println("operate方法的后置处理");
}
}
运行结果:
operate方法的预处理
真实主题的处理方法...
第0个参数:5
第1个参数:10
operate方法的后置处理
operate方法的返回值:15
问题:代理对象proxy调用operate方法为何会执行InvocationHandler接口实现类的invoke方法?
可以通过分析运行时生成的代理对象对应的代理类得知,在main方法里生成代理对象代码前,增加下面代码,用于设置保存在程序运行期生成的jdk动态代理类的class文件:
//Java8设置保存在程序运行期生成的jdk动态代理类的class文件
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
IntelliJ IDEA自动反编译出的代理类如下,从代码中可以知道:生成的代理类继承java.lang.reflect.Proxy
类,实现了AbstractSubject抽象主题接口,上面生成的代理对象proxy就是类
P
r
o
x
y
0
的
实
例
。
程
序
运
行
调
用
代
理
类
Proxy0的实例。程序运行调用代理类
Proxy0的实例。程序运行调用代理类Proxy0 的构造器 public
P
r
o
x
y
0
(
I
n
v
o
c
a
t
i
o
n
H
a
n
d
l
e
r
v
a
r
1
)
时
会
调
用
父
类
构
造
器
,
传
入
I
n
v
o
c
a
t
i
o
n
H
a
n
d
l
e
r
接
口
的
实
现
类
对
象
。
代
理
类
Proxy0(InvocationHandler var1) 时会调用父类构造器,传入InvocationHandler接口的实现类对象。代理类
Proxy0(InvocationHandlervar1)时会调用父类构造器,传入InvocationHandler接口的实现类对象。代理类Proxy0的operate方法中有这样一句调用代码:super.h.invoke(this, m3, new Object[]{var1, var2})
,因此会执行InvocationHandler接口的实现类的invoke方法,这里的 this 即代理对象proxy,m3即operate方法的method实例,var1、var2即调用operate方法传入的实参5和10。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package v4;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
final class $Proxy0 extends Proxy implements AbstractSubject {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final int operate(int var1, int var2) throws {
try {
return (Integer)super.h.invoke(this, m3, new Object[]{var1, var2});
} catch (RuntimeException | Error var4) {
throw var4;
} catch (Throwable var5) {
throw new UndeclaredThrowableException(var5);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("v4.AbstractSubject").getMethod("operate", Integer.TYPE, Integer.TYPE);
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}