前言
有时,我们想要给某对象提供一个代理对象,以控制对该对象的访问。代理对象应该作为访问对象和目标对象之间的中介。
Java中的代理可以分为静态代理和动态代理。静态代理类在编译期间就生成,动态代理类在Java运行时动态产生。动态代理又分为JDK代理和cglib代理两种。
原文地址:https://xuedongyun.cn/post/23348/
代理模式的角色
代理(Proxy)模式分为三种角色:
- 抽象主题类(Subject):声明需要实现的业务方法
- 真实主题类(Real Subject):实现抽象主题类。是代理对象所代表的真实对象
- 代理类(Proxy):实现抽象主题类。内部有对真实主题类的引用
静态代理
抽象主题类(Subject)
1 2 3
| public interface SellTickets { void sell(); }
|
真实主题类(Real Subject)
1 2 3 4 5 6
| public class TrainStation implements SellTickets { @Override public void sell() { System.out.println("车站买票"); } }
|
代理类(Proxy)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class ProxyPoint implements SellTickets {
private SellTickets target;
public ProxyPoint(SellTickets target) { this.target = target; } @Override public void sell() { System.out.println("before"); target.sell(); System.out.println("after"); } }
|
使用代理
1 2 3
| TrainStation trainStation = new TrainStation(); ProxyPoint proxy = new ProxyPoint(trainStation); proxy.sell();
|
JDK动态代理
Java中提供了一个动态代理类Proxy
,其中有一个newProxyInstance
方法用来获取代理对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| public class ProxyFactory {
private TrainStation station = new TrainStation();
public ProxyFactory(TrainStation station) { this.station = station; }
public SellTickets getProxyObject() { SellTickets proxyInstance = (SellTickets) Proxy.newProxyInstance(station.getClass().getClassLoader(), station.getClass().getInterfaces(), new InvocationHandler() {
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before"); Object res = method.invoke(station, args); System.out.println("after"); return res; } }); return proxyInstance; } }
|
使用代理
1 2 3 4 5 6
| TrainStation trainStation = new TrainStation();
ProxyFactory proxyFactory = new ProxyFactory(trainStation); SellTickets proxy = proxyFactory.getProxyObject();
proxy.sell();
|
原理分析如下:
Java创建了com.sun.proxy.$Proxy0
类作为代理类,是程序在运行中动态的在内存中创建的,我们摘取重点代码来查看
执行时流程如下:
- 测试时,调用代理对象的sell方法
- 代理对象($Proxy0)中的sell方法,调用
invocationHandler
实现类的sell方法(此时传入了this,也即前面提到的proxy参数) invocationHandler
实现类中,使用反射执行了真实对象的sell方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 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); } }
|
CGLIB动态代理
JDK动态代理要求被代理的对象必须实现接口。CGLIB是一个功能强大的第三方包,能为没有实现接口的类提供代理
需要先引入依赖
1 2 3 4 5
| <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency>
|
截至目前为止,官方的CGLIB任然没有更新,无法在JDK17下使用。可以使用Spring中官方修改过的CGLIB来代替
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| public class ProxyFactory implements MethodInterceptor {
private TrainStation station;
public ProxyFactory(TrainStation station) { this.station = station; }
public TrainStation getProxyObject() { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(station.getClass()); enhancer.setCallback(this); return (TrainStation) enhancer.create(); }
@Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("before"); TrainStation res = (TrainStation) methodProxy.invokeSuper(o, objects); System.out.println("after"); return res; } }
|
两种代理的对比
- JDK代理 VS CGLIB 代理
- CGLIB底层采用ASM字节码生成框架,使用字节码生成代理类。在JDK1.6之前比反射效率高。CGLIB的原理为生成被代理对象的子类。(因此不能代理final修饰的类)
- 目前,JDK代理效率高于CGLIB。所以有接口类的使用JDK代理,没接口的类使用CGLIB