🍃前言
前面对Spring AOP的详细使用进行了介绍,这篇博客博主将详细讲解一下Spring AOP的原理,也就是Spring是如何实现AOP的.
Spring AOP是基于动态代理来实现AOP的,动态代理又是代理模式的一种
接下来我们一起来看看代理模式
🎋什么叫代理模式
代理模式,也叫委托模式.
定义:为其他对象提供⼀种代理以控制对这个对象的访问.它的作⽤就是通过提供⼀个代理类,让我们
在调⽤⽬标⽅法的时候,不再是直接对目标⽅法进行调用,而是通过代理类间接调用.
在某些情况下,⼀个对象不适合或者不能直接引⽤另⼀个对象,而代理对象可以在客⼾端和目标对象之
间起到中介的作用
使用代理前:
使用代理后:
生活中其实也存在着代理模式:
- 艺人经纪⼈:光告商找艺⼈拍⼴告,需要经过经纪⼈,由经纪⼈来和艺⼈进行沟通.
- 房屋中介:房屋进行租赁时,卖⽅会把房屋授权给中介,由中介来代理看房,房屋咨询等服务.
- 经销商:⼚商不直接对外销售产品,由经销商负责代理销售.
- 秘书/助理:合作伙伴找⽼板谈合作,需要先经过秘书/助理预约.
接下来我们来一起认识一下代理模式里的主要角色
- Subject:业务接⼝类.可以是抽象类或者接⼝(不⼀定有)
- RealSubject:业务实现类.具体的业务执⾏,也就是被代理对象.
- Proxy:代理类.RealSubject的代理
如果看成祖房
Subject:就是提前定义了房东做的事情,交给中介代理,也是中介要做的事情
RealSubject:房东
Proxy:中介
代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行⼀些功能的附加与增强.
根据代理的创建时期,代理模式分为静态代理和动态代理.
- 静态代理:由程序员创建代理类或特定⼯具⾃动⽣成源代码再对其编译,在程序运⾏前代理类的
.class
⽂件就已经存在了. - 动态代理:在程序运⾏时,运⽤反射机制动态创建⽽成.
🌴静态代理
静态代理: 在程序运⾏前,代理类的.class
⽂件就已经存在了.(在出租房⼦之前,中介已经做好了相关的
⼯作,就等租⼾来租房⼦了)
虽然静态代理也完成了对目标对象的代理,但是由于代码都写死了,对⽬标对象的每个⽅法的增强都是⼿动完成的,⾮常不灵活.所以⽇常开发⼏乎看不到静态代理的场景
如果目标对象新增其他业务,则需要我们手动进行修改,同样的,如果有新增接(Subject)和业务实现类(RealSubject),也需要对每⼀个业务实现类新增代理类(Proxy).
既然代理的流程是⼀样的,有没有⼀种办法,让他们通过⼀个代理类来实现呢?
这就需要⽤到动态代理技术了
🎍动态代理
相比于静态代理来说,动态代理更加灵活.
我们不需要针对每个目标对象都单独创建⼀个代理对象,而是把这个创建代理对象的⼯作推迟到程序运
行时由JVM来实现.也就是说动态代理在程序运行时,根据需要动态创建⽣成.
比如房屋中介,我不需要提前预测都有哪些业务,而是业务来了我再根据情况创建.
Java也对动态代理进⾏了实现,并给我们提供了⼀些API,常见的实现方式有两种:
- JDK动态代理
- CGLIB动态代理
🚩JDK动态代理
JDK动态代理类实现步骤
- 定义⼀个接⼝及其实现类(静态代理中的 HouseSubject 和 RealHouseSubject )
- ⾃定义 InvocationHandler 并重写 invoke ⽅法,在 invoke ⽅法中我们会调⽤⽬标⽅
法(被代理类的⽅法)并⾃定义⼀些处理逻辑 - 通过
javaProxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
⽅法创建代理对象
下面我们来进行实现以下:
- 定义JDK动态代理类
实现 InvocationHandler 接⼝
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class JDKInvocationHandler implements InvocationHandler { //⽬标对象即就是被代理对象 private Object target; public JDKInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Thro // 代理增强内容 System.out.println("我是中介, 开始代理"); //通过反射调⽤被代理类的⽅法 Object retVal = method.invoke(target, args); //代理增强内容 System.out.println("我是中介, 代理结束"); return retVal; } }
- 创建⼀个代理对象并使用
public class DynamicMain { public static void main(String[] args) { HouseSubject target= new RealHouseSubject(); //创建⼀个代理类:通过被代理类、被代理实现的接⼝、⽅法调⽤处理器来创建 HouseSubject proxy = (HouseSubject) Proxy.newProxyInstance( target.getClass().getClassLoader(), new Class[]{HouseSubject.class}, new JDKInvocationHandler(target) ); proxy.rentHouse(); } }
对上述代码做一个简单的讲解:
- InvocationHandler接是Java动态代理的关键接⼝之⼀,它定义了⼀个单⼀⽅法 invoke() ,用于处理被代理对象的方法调用.通过实现 InvocationHandler 接⼝,可以对被代理对象的方法进⾏功能增强.
- Proxy 类中使⽤频率最高的⽅法是: newProxyInstance() ,这个⽅法主要⽤来⽣成⼀个代理对象
- 这个⽅法⼀共有3个参数:
- Loader:类加载器,⽤于加载代理对象.
- interfaces:被代理类实现的⼀些接⼝(这个参数的定义,也决定了JDK动态代理只能代理实现了接⼝的⼀些类)
- h:实现了InvocationHandler接⼝的对象
🚩CGLIB动态代理
JDK 动态代理有⼀个最致命的问题是其只能代理实现了接⼝的类.
有些场景下,我们的业务代码是直接实现的,并没有接⼝定义.为了解决这个问题,我们可以⽤CGLIB动
态代理机制来解决.
CGLIB(Code Generation Library)是⼀个基于ASM的字节码⽣成库,它允许我们在运⾏时对字节码进⾏
修改和动态⽣成.CGLIB通过继承⽅式实现代理,很多知名的开源框架都使⽤到了CGLIB.
例如:Spring中的AOP模块中:如果⽬标对象实现了接⼝,则默认采⽤JDK动态代理,否则采⽤CGLIB动态代理.
CGLIB动态代理类实现步骤
- 定义⼀个类(被代理类)
- ⾃定义 MethodInterceptor 并重写 intercept ⽅法, intercept ⽤于增强⽬标⽅法,和JDK动态代理中的 invoke ⽅法类似
- 通过Enhancer类的create()创建代理类
接下来一起来看一下实一个简单的实现:
- 添加依赖
和JDK动态代理不同,CGLIB(Code Generation Library)实际是属于⼀个开源项⽬,如果你要使用它
的话,需要⼿动添加相关依赖
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency>
- ⾃定义MethodInterceptor(⽅法拦截器)
实现MethodInterceptor接口
import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class CGLIBInterceptor implements MethodInterceptor { //⽬标对象, 即被代理对象 private Object target; public CGLIBInterceptor(Object target){ this.target = target; } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { // 代理增强内容 System.out.println("我是中介, 开始代理"); //通过反射调⽤被代理类的⽅法 Object retVal = methodProxy.invoke(target, objects); //代理增强内容 System.out.println("我是中介, 代理结束"); return retVal; } }
- 创建代理类,并使用
public class DynamicMain { public static void main(String[] args) { HouseSubject target= new RealHouseSubject(); HouseSubject proxy= (HouseSubject) Enhancer.create(target.getClass(),new CGLIBInterceptor(target)); proxy.rentHouse(); } }
代码简单讲解如下:
- MethodInterceptor 和JDK动态代理中的 InvocationHandler 类似,它只定义了⼀个方法 intercept() ,用于增强目标⽅法
- Enhancer.create()用来⽣成⼀个代理对象
- 参数说明:
- type:被代理类的类型(类或接⼝)
- callback:自定义⽅法拦截器MethodInterceptor
⭕总结
关于《【JavaEE进阶】 代理模式》就讲解到这儿,感谢大家的支持,欢迎各位留言交流以及批评指正,如果文章对您有帮助或者觉得作者写的还不错可以点一下关注,点赞,收藏支持一下!