给某个对象提供一个代理对象来控制对该对象的访问。访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象的中介存在。
如果要买火车票,需要去火车站买票,坐车到火车站,排队等一系列操作,这样的操作比较麻烦。而火车站在多个地方都有代售点,去代售点买票就会方便很多。这个例子就是典型的代理模式,其中「火车站」就是「目标对象」,而「代售点」就是「代理对象」。
public interface SellTickets {
// 售票接口(抽象主题类)
void sell();
}
public class TrainStation implements SellTickets {
// 火车站(真实主题类)
@Override
public void sell() {
System.out.println("火车站卖票");
}
}
public class ProxyPoint implements SellTickets {
// 代售点(代理类)
private TrainStation trainStation = new TrainStation();
@Override
public void sell() {
System.out.println("代售点收取一些服务费用");
trainStation.sell();
}
}
public class Client {
public static void main(String[] args) {
ProxyPoint proxyPoint = new ProxyPoint();
proxyPoint.sell();
}
}
java中提供了一个动态代理类Proxy,Proxy并不是上述中的「代理对象」的类,而是提供了一个创建代理对象的静态方法newProxyInstance
来获取代理对象。
public class ProxyFactory {
private TrainStation station = new TrainStation();
public SellTickets getProxyObject() {
//使用Proxy获取代理对象
/*
newProxyInstance()方法参数说明:
ClassLoader loader : 类加载器,用于加载代理类,使用真实对象的类加载器即可
Class>[] interfaces : 真实对象所实现的接口,代理模式真实对象和代理对象实现相同的接口
InvocationHandler h : 代理对象的调用处理程序
*/
SellTickets sellTickets = (SellTickets) Proxy.newProxyInstance(
station.getClass().getClassLoader(),
station.getClass().getInterfaces(),
new InvocationHandler() {
/*
InvocationHandler中invoke方法参数说明:
proxy : 代理对象
method : 对应于在代理对象上调用的接口方法的 Method 实例
args : 代理对象调用接口方法时传递的实际参数
*/
// 可以在此处对方法进行增强
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理点收取一些费用(JDK动态代理方式)");
// 执行真实对象的方法
Object obj = method.invoke(station, args);
return obj;
}
}
);
return sellTickets;
}
}
ProxyFactory是代理类吗?
$Proxy0的结构:
代理类($Proxy0)实现了SellTickets
,说明「真实类」和「代理类」会实现相同的接口;
代理类($Proxy0)将提供的匿名内部类对象传递给了父类。
package com.sun.proxy;
import com.itheima.proxy.dynamic.jdk.SellTickets;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements SellTickets {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy0(InvocationHandler invocationHandler) {
super(invocationHandler);
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("com.itheima.proxy.dynamic.jdk.SellTickets").getMethod("sell", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException noSuchMethodException) {
throw new NoSuchMethodError(noSuchMethodException.getMessage());
}
catch (ClassNotFoundException classNotFoundException) {
throw new NoClassDefFoundError(classNotFoundException.getMessage());
}
}
public final boolean equals(Object object) {
try {
return (Boolean)this.h.invoke(this, m1, new Object[]{object});
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final String toString() {
try {
return (String)this.h.invoke(this, m2, null);
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final int hashCode() {
try {
return (Integer)this.h.invoke(this, m0, null);
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final void sell() {
try {
this.h.invoke(this, m3, null);
return;
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
}
动态代理的执行流程
sell()
方法$Proxy()
中的sell()
方法$Proxy()
中的sell()
方法又调用了InvocationHandler
接口的子实现类对象的invoke
方法invoke
方法通过反射执行了真实对象所属类TrainStation
中的sell()
方法//程序运行过程中动态生成的代理类
public final class $Proxy0 extends Proxy implements SellTickets {
private static Method m3;
public $Proxy0(InvocationHandler invocationHandler) {
super(invocationHandler);
}
static {
m3 = Class.forName("com.itheima.proxy.dynamic.jdk.SellTickets").getMethod("sell", new Class[0]);
}
public final void sell() {
this.h.invoke(this, m3, null);
}
}
//Java提供的动态代理相关类
public class Proxy implements java.io.Serializable {
protected InvocationHandler h;
protected Proxy(InvocationHandler h) {
this.h = h;
}
}
//代理工厂类
public class ProxyFactory {
private TrainStation station = new TrainStation();
public SellTickets getProxyObject() {
SellTickets sellTickets = (SellTickets) Proxy.newProxyInstance(station.getClass().getClassLoader(),
station.getClass().getInterfaces(),
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理点收取一些服务费用(JDK动态代理方式)");
Object result = method.invoke(station, args);
return result;
}
});
return sellTickets;
}
}
//测试访问类
public class Client {
public static void main(String[] args) {
//获取代理对象
ProxyFactory factory = new ProxyFactory();
SellTickets proxyObject = factory.getProxyObject();
proxyObject.sell();
}
}
如果没有定义SellTickets()
接口,只定义了TrainStation()
火车站类,则无法使用JDK代理,因为JDK动态代理必须定义接口,对接口进行代理。
CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,补充了JDK代理。
CGLIB是第三方提供的包,需要引入Jar包的坐标
<dependency>
<groupId>cglib</groupId>
<artifarufuctId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
public class TrainStation {
// 火车站
public void sell() {
System.out.println("火车站卖票");
}
}
public class ProxyFactory implements MethodInterceptor {
private TrainStation target = new TrainStation();
public TrainStation getProxyObject(){
// 1 创建一个Enhancer对象
Enhancer enhancer = new Enhancer();
// 2 设置父类的字节码对象
enhancer.setSuperclass(target.getClass());
// 3 设置回调函数
enhancer.setCallback(this);
//4 创建代理对象
TrainStation obj = (TrainStation) enhancer.create();
return obj;
}
/*
intercept方法参数说明:
o : 代理对象
method : 真实对象中的方法的Method实例
args : 实际参数
methodProxy :代理对象中的方法的method实例
*/
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("代理点收取一些服务费用(CGLIB动态代理方式)");
TrainStation result = (TrainStation) methodProxy.invokeSuper(o, args);
return result;
}
}
public class Client {
public static void main(String[] args) {
// 1 创建代理工厂对象
ProxyFactory proxyFactory = new ProxyFactory();
// 2 创建代理对象
TrainStation proxyObject = proxyFactory.getProxyObject();
// 3 调用代理对象的方法
proxyObject.sell();
}
}
Java中的动态代理机制主要有两种:基于
java.lang.reflect
包的JDK动态代理,以及基于第三方库如CGLIB的动态代理。
特性 | 静态代理 | 动态代理 |
---|---|---|
生成方式 | 编译时生成 | 运行时生成 |
实现复杂度 | 简单,易于理解 | 复杂,需要理解反射机制 |
代码复用性 | 低,需要为每个目标类编写代理类 | 高,可以动态处理多个目标对象 |
维护成本 | 高,代理类多 | 低,不需要手动编写多个代理类 |
性能 | 高,没有反射的性能开销 | 较低,通过反射机制调用方法 |
静态代理适用于目标对象较少,且代理逻辑简单明确的场景。动态代理适用于目标对象较多,希望提高代码复用性,且可以接受一些性能开销的场景。
java.lang.reflect.Proxy
和java.lang.reflect.InvocationHandler
类实现。cglib-nodep-x.x.x.jar
)才能使用。final
类,因为动态代理的实现方式需要生成目标类的子类。final
方法,否则也无法被重写。特性 | JDK动态代理 | CGLIB动态代理 |
---|---|---|
代理机制 | 基于接口 | 基于生成子类 |
目标对象要求 | 必须实现至少一个接口 | 可以不实现接口,但不能为final类 |
依赖 | 无需额外依赖,Java自带 | 需要额外引入CGLIB库 |
性能 | 一般较低,尤其是在大量方法调用时性能欠佳 | 通常较高,处理大量方法调用时性能优于JDK动态代理 |
使用难度 | 较低,适合简单场景 | 较复杂,需了解第三方库及其配置 |
灵活性 | 只能代理实现了接口的目标对象 | 可代理任何普通类,使用范围更广 |
选择哪种代理方式要依据具体的应用场景来决定。如果项目中目标对象实现了接口,并且调用频率不是特别高,JDK动态代理是一个简便且高效的选择。如果需要代理的类没有实现接口或者需要处理大量的方法调用场景,CGLIB动态代理可能是更好的选择。
远程代理(Remote Proxy)
目的:为在不同地址空间中的对象提供本地代理,处理远程对象的方法调用和通信。
应用场景:
分布式系统:客户端通过代理对象与远程服务器通信,代理对象负责执行远程过程调用(RPC)。
Web服务调用:客户端使用代理对象来调用远程的Web服务,代理对象负责处理底层的HTTP请求和响应。
虚拟代理(Virtual Proxy)
目的:通过代理延迟创建资源消耗较高的对象,只有在需要时才进行对象的实际创建。
应用场景:
保护代理(Protection Proxy)
目的:控制对原始对象的访问权限,进行权限检查或访问控制。
应用场景: