【JavaEE进阶】——Spring AOP 实现原理
目录
🚩代理模式的定义和理解
🚩代理模式的主要⻆⾊
🚩代理模式的分类
🎈静态代理
🎈动态代理
🎓JDK动态代理
📝JDK 动态代理类实现步骤
🎓CGLIB动态代理
📝CGLIB 动态代理类实现步骤
🎈JDK代理和CGLIB代理
🚩Spring AOP总结
上一篇文章我们主要讲述spring AOP的使用,我们现在来讲讲Spring AOP的原理,也就是如何实现Spring AOP的。
Spring AOP是基于动态代理来实现AOP的。
🚩代理模式的定义和理解
代理模式, 也叫委托模式. 定义: 为其他对象提供⼀种代理以控制对这个对象的访问. 它的作⽤就是通过提供⼀个代理类, 让我们在调⽤⽬标⽅法的时候, 不再是直接对⽬标⽅法进⾏调⽤, ⽽是通过代理类间接调⽤. 在某些情况下, ⼀个对象不适合或者不能直接引⽤另⼀个对象, ⽽代理对象可以在客⼾端和⽬标对象之 间起到中介的作⽤。 为其他对象(房东)提供一种代理(中介)以控制对这个对象访问,作用就是提供一个中介来对买房子的人进行处理。 这就像租房一样 正常的流程是:租户->房东 代理流程:租房->中介->房东 (中介就是代理) 适用场景:1.无法直接调用目标对象 2.目标对象给我们提供的功能不够,我们希望对目标对象提供的方法进行功能的增强再不改变原方法的情况下使用代理前:
使用代理后:生活中的代理:
- 艺⼈经纪⼈: ⼴告商找艺⼈拍⼴告, 需要经过经纪⼈,由经纪⼈来和艺⼈进⾏沟通.
- • 房屋中介: 房屋进⾏租赁时, 卖⽅会把房屋授权给中介, 由中介来代理看房, 房屋咨询等服务.
- • 经销商: ⼚商不直接对外销售产品, 由经销商负责代理销售.
- • 秘书/助理: 合作伙伴找⽼板谈合作, 需要先经过秘书/助理预约.
🚩代理模式的主要⻆⾊
- 1. Subject: 业务接⼝类. 可以是抽象类或者接⼝(不⼀定有)
- 2. RealSubject: 业务实现类. 具体的业务执⾏, 也就是被代理对象.
- 3. Proxy: 代理类. RealSubject的代理.
Subject 就是提前定义了房东做的事情, 交给中介代理, 也是中介要做的事情
RealSubject: 房东
Proxy: 中介
🚩代理模式的分类
根据代理的创建时期, 代理模式分为 静态代理和动态代理- 静态代理: 由程序员创建代理类或特定⼯具⾃动⽣成源代码再对其编译, 在程序运⾏前代理类的 .class ⽂件就已经存在了.(程序运行前创建)
- 动态代理: 在程序运⾏时, 运⽤反射机制动态创建⽽成 (程序运行后创建)
🎈静态代理
静态代理: 在程序运⾏前, 代理类的 .class⽂件就已经存在了. (在出租房⼦之前, 中介已经做好了相关的⼯作, 就等租⼾来租房⼦了) 我们通过代码来加深理解. 以房租租赁为例 定义接口(定义房东要做的事情, 也是中介需要做的事情)/** * 业务接口类 */ public interface HouseSubject { void rentHouse(); void saleHouse(); }
实现接口(房东出租房子)
/** * 业务实现类 */ public class RealHouseSubject implements HouseSubject{ @Override public void rentHouse() { System.out.println("我是房东,我要出租房子"); } @Override public void saleHouse() { System.out.println("我是房东,我要出售房子"); } }
代理(中介,帮房东出租房子)
/** * 静态代理的代理类 */ public class HouseProxy implements HouseSubject{ private HouseSubject target; public HouseProxy(HouseSubject target) { this.target = target; } @Override public void rentHouse() { //代理前 System.out.println("我是中介,开始代理"); //出租房子 target.rentHouse(); //代理后 System.out.println("我是中介,结束代理"); } @Override public void saleHouse() { //代理前 System.out.println("我是中介,开始代理"); //出租房子 target.saleHouse(); //代理后 System.out.println("我是中介,结束代理"); } }
使⽤public class Main { public static void main(String[] args) { //代理中介 HouseProxy proxy=new HouseProxy(new RealHouseSubject()); proxy.rentHouse(); } }
上⾯这个代理实现⽅式就是静态代理(仿佛啥也没⼲). 如果对中介新增卖房代理,那么就要新加接口,并且要再房东类和中介类中加入卖房的实现。 从上述代码可以看出, 我们修改接⼝(Subject)和业务实现类(RealSubject)时, 还需要修改代理类 (Proxy). 同样的, 如果有新增接⼝(Subject)和业务实现类(RealSubject), 也需要对每⼀个业务实现类新增代理类 (Proxy)。 从上述程序可以看出, 虽然静态代理也完成了对⽬标对象的代理, 但是由于代码都写死了, 对⽬标对象的每个⽅法的增强都是⼿动完成的,⾮常不灵活. 所以⽇常开发⼏乎看不到静态代理的场景. 既然代理的流程是⼀样的, 有没有⼀种办法, 让他们通过⼀个代理类来实现呢? 这就需要⽤到动态代理技术了。🎈动态代理
相⽐于静态代理来说,动态代理更加灵活. 我们不需要针对每个⽬标对象都单独创建⼀个代理对象, ⽽是把这个创建代理对象的⼯作推迟到程序运⾏时由JVM来实现. 也就是说动态代理在程序运⾏时, 根据需要动态创建⽣成 ⽐如房屋中介, 我不需要提前预测都有哪些业务, ⽽是业务来了我再根据情况创建. Java也对动态代理进⾏了实现, 并给我们提供了⼀些API, 常⻅的实现⽅式有两种:- 1. JDK动态代理
- 2. CGLIB动态代理
动态代理在我们⽇常开发中使⽤的相对较少,但是在框架中⼏乎是必⽤的⼀⻔技术. 学会了动态代理之后, 对于我们理解和学习各种框架的原理也⾮常有帮助
🎓JDK动态代理
📝JDK 动态代理类实现步骤
- 1. 定义⼀个接⼝及其实现类(静态代理中的 HouseSubject 和 RealHouseSubject )
- 2. ⾃定义 InvocationHandler 并重写 invoke ⽅法,在 invoke ⽅法中我们会调⽤⽬标⽅法(被代理类的⽅法)并⾃定义⼀些处理逻辑
- 3. 通过Proxy.newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h) ⽅法创建代理对象
定义JDK动态代理类 .实现 InvocationHandler 接⼝
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 Throwable { //代理增强内容 System.out.println("我是代理,开始代理"); //通过反射,调用目标对象的方法 Object result=method.invoke(target,args); //代理增强内容 System.out.println("我是代理,结束代理"); return result; } }
创建⼀个代理对象并使⽤/** * JDK动态代理 * @param args */ public static void main(String[] args) { //JDK动态代理句式 // /** newProxyInstance(ClassLoader loader, // Class[] interfaces, // InvocationHandler h) { // *loader 加载我们的代理类的classload (房东) // *interfaces 要实现的接口 (业务接口) // *h 代理要做的事情,需要实现InvocationHandler这个接口 (房东做的事情) // */ //目标对象 RealHouseSubject target=new RealHouseSubject();//房东 //动态生成代理对象 HouseSubject proxy= (HouseSubject) Proxy.newProxyInstance( target.getClass().getClassLoader(), new Class[]{HouseSubject.class}, new JDKInvocationHandler(target)); proxy.rentHouse(); }
🎓CGLIB动态代理
JDK 动态代理有⼀个最致命的问题是其只能代理实现了接⼝的类 有些场景下, 我们的业务代码是直接实现的, 并没有接⼝定义. 为了解决这个问题, 我们可以⽤ CGLIB 动态代理机制来解决..(也就是说JDK动态代理只能是接口,不能是类) Spring中的 AOP 模块中: 如果⽬标对象实现了接⼝,则默认采⽤ JDK 动态代理, 否则采⽤ CGLIB 动态代理📝CGLIB 动态代理类实现步骤
- 1. 定义⼀个类(被代理类)
- 2. ⾃定义 MethodInterceptor 并重写 intercept ⽅法, intercept ⽤于增强⽬标⽅ 法,和 JDK 动态代理中的 invoke ⽅法类似
- 3. 通过 Enhancer 类的 create()创建代理类
添加依赖
cglib cglib 3.3.0
⾃定义 MethodInterceptor(⽅法拦截器)实现MethodInterceptor接⼝public class CGLibMethodIntercept implements MethodInterceptor { private Object target; public CGLibMethodIntercept(Object target) { this.target = target; } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("我是中介,开始代理"); Object result=method.invoke(target,args); System.out.println("我是中介,结束代理"); return result; } }
创建代理类, 并使⽤/** * CGLIB动态代理 * @param args */ public static void main(String[] args) { RealHouseSubject realHouseSubject= (RealHouseSubject) Enhancer.create(target.getClass(),new CGLibMethodIntercept(target)); realHouseSubject.rentHouse(); }
🎈JDK代理和CGLIB代理
📝JDK可以代理接口(HouseSubject),不可以代理类(RealHouseSubject) 📝CGLib可以代理接口(HouseSubject),又可以代理类(RealHouseSubject) Spring 默认proxyTargetClass false 默认JDK代理- 实现了接口 使用JDK代理
- 普通类 使用CGLib代理
Spring Boot从2.x之后
proxyTargetClass true 默认使用CGLib代理
#设置CGLib代理 spring: aop: proxy-target-class: true
public static void main(String[] args) { ApplicationContext context=SpringApplication.run(SpringAopApplication.class, args); //代理类 TestController bean=context.getBean(TestController.class); System.out.println(bean); }
public static void main(String[] args) { ApplicationContext context=SpringApplication.run(SpringAopApplication.class, args); //代理接口 Iface iface= (Iface) context.getBean("testController2"); System.out.println(iface); }
#设置JDK代理 spring: aop: proxy-target-class: false #JDK代理
🚩Spring AOP总结
- 1. AOP是⼀种思想, 是对某⼀类事情的集中处理. Spring框架实现了AOP, 称之为SpringAOP
- 2. Spring AOP常⻅实现⽅式有两种: 1. 基于注解@Aspect来实现 2. 基于⾃定义注解来实现, 还有⼀些更原始的⽅式,⽐如基于代理, 基于xml配置的⽅式, 但⽬标⽐较少⻅
- 3. Spring AOP 实现原理:是基于动态代理实现的, 有两种⽅式: 1. 基本JDK动态代理实现 2. 基于CGLIB动态代理实现. 运⾏时使⽤哪种⽅式与项⽬配置和代理的对象有关
如果事事不能圆满,那椭圆也行。
文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。