摘要:本文主要讲了Spring Aop动态代理实现的两种方式。

1. Spring AOP

Spring是一个轻型容器,Spring整个系列的最最核心的概念当属IoC、AOP。可见AOP是Spring框架中的核心之一,在应用中具有非常重要的作用,也是Spring其他组件的基础。AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。

关于AOP的基础知识,并不是本文的重点,我们主要来看下AOP的核心功能的底层实现机制:动态代理的实现原理。AOP的拦截功能是由java中的动态代理来实现的。在目标类的基础上增加切面逻辑,生成增强的目标类(该切面逻辑或者在目标类函数执行之前,或者目标类函数执行之后,或者在目标类函数抛出异常时候执行。不同的切入时机对应不同的Interceptor的种类,如BeforeAdviseInterceptor,AfterAdviseInterceptor以及ThrowsAdviseInterceptor等)。

那么动态代理是如何实现将切面逻辑(advise)织入到目标类方法中去的呢?下面我们就来详细介绍并实现AOP中用到的两种动态代理。

AOP的源码中用到了两种动态代理来实现拦截切入功能:jdk动态代理和cglib动态代理。两种方法同时存在,各有优劣。jdk动态代理是由java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。

下面我们分别来示例实现这两种方法。

2. JDK动态代理

2.1 定义接口与实现类

public interface OrderService {public void save(UUID orderId, String name);public void update(UUID orderId, String name);public String getByName(String name);
}

上面代码定义了一个被拦截对象接口,即横切关注点。下面代码实现被拦截对象接口。

public class OrderServiceImpl implements OrderService {private String user = null;public OrderServiceImpl() {}public OrderServiceImpl(String user) {this.setUser(user);}//...@Overridepublic void save(UUID orderId, String name) {System.out.println("call save()方法,save:" + name);}@Overridepublic void update(UUID orderId, String name) {System.out.println("call update()方法");}@Overridepublic String getByName(String name) {System.out.println("call getByName()方法");return "aoho";}
}

2.2 JDK动态代理类

public class JDKProxy implements InvocationHandler {//需要代理的目标对象private Object targetObject;public Object createProxyInstance(Object targetObject) {this.targetObject = targetObject;return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),this.targetObject.getClass().getInterfaces(), this);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//被代理对象OrderServiceImpl bean = (OrderServiceImpl) this.targetObject;Object result = null;//切面逻辑(advise),此处是在目标类代码执行之前System.out.println("---before invoke----");if (bean.getUser() != null) {result = method.invoke(targetObject, args);}System.out.println("---after invoke----");return result;}//...}

上述代码实现了动态代理类JDKProxy,实现InvocationHandler接口,并且实现接口中的invoke方法。当客户端调用代理对象的业务方法时,代理对象执行invoke方法,invoke方法把调用委派给targetObject,相当于调用目标对象的方法,在invoke方法委派前判断权限,实现方法的拦截。

2.3 测试

public class AOPTest {public static void main(String[] args) {JDKProxy factory = new JDKProxy();//Proxy为InvocationHandler实现类动态创建一个符合某一接口的代理实例  OrderService orderService = (OrderService) factory.createProxyInstance(new OrderServiceImpl("aoho"));//由动态生成的代理对象来orderService 代理执行程序orderService.save(UUID.randomUUID(), "aoho");}}

结果如下:

---before invoke----
call save()方法,save:aoho
---after invoke----

3. CGLIB字节码生成

3.1 要代理的类

CGLIB既可以对接口的类生成代理,也可以针对类生成代理。示例中,实现对类的代理。

public class OrderManager {private String user = null;public OrderManager() {}public OrderManager(String user) {this.setUser(user);}//...public void save(UUID orderId, String name) {System.out.println("call save()方法,save:" + name);}public void update(UUID orderId, String name) {System.out.println("call update()方法");}public String getByName(String name) {System.out.println("call getByName()方法");return "aoho";}
}

该类的实现和上面的接口实现一样,为了保持统一。

3.2 CGLIB动态代理类

public class CGLibProxy implements MethodInterceptor {// CGLib需要代理的目标对象private Object targetObject;public Object createProxyObject(Object obj) {this.targetObject = obj;Enhancer enhancer = new Enhancer();enhancer.setSuperclass(obj.getClass());//回调方法的参数为代理类对象CglibProxy,最后增强目标类调用的是代理类对象CglibProxy中的intercept方法 enhancer.setCallback(this);//增强后的目标类Object proxyObj = enhancer.create();// 返回代理对象return proxyObj;}@Overridepublic Object intercept(Object proxy, Method method, Object[] args,MethodProxy methodProxy) throws Throwable {Object obj = null;//切面逻辑(advise),此处是在目标类代码执行之前System.out.println("---before intercept----");obj = method.invoke(targetObject, args);System.out.println("---after intercept----");return obj;}
}

上述实现了创建子类的方法与代理的方法。getProxy(SuperClass.class)方法通过入参即父类的字节码,扩展父类的class来创建代理对象。intercept()方法拦截所有目标类方法的调用,obj表示目标类的实例,method为目标类方法的反射对象,args为方法的动态入参,methodProxy为代理类实例。method.invoke(targetObject, args)通过代理类调用父类中的方法。

3.3 测试

public class AOPTest {public static void main(String[] args) {OrderManager order = (OrderManager) new CGLibProxy().createProxyObject(new OrderManager("aoho"));order.save(UUID.randomUUID(), "aoho");}

结果如下:

---before intercept----
call save()方法,save:aoho
---after intercept----

4. 总结

本文主要讲了Spring Aop动态代理实现的两种方式,并分别介绍了其优缺点。jdk动态代理的应用前提是目标类基于统一的接口。如果没有该前提,jdk动态代理不能应用。由此可以看出,jdk动态代理有一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。

JDK动态代理机制是委托机制,不需要以来第三方的库,只要要JDK环境就可以进行代理,动态实现接口类,在动态生成的实现类里面委托为hanlder去调用原始实现类方法;CGLib 必须依赖于CGLib的类库,使用的是继承机制,是被代理类和代理类继承的关系,所以代理类是可以赋值给被代理类的,如果被代理类有接口,那么代理类也可以赋值给接口。

参考

  1. jdk动态代理代理与cglib代理原理探究
  2. AOP的底层实现-CGLIB动态代理和JDK动态代理

本文分享自华为云社区《还不懂Spring AOP?一文带你搞懂动态代理》,原文作者:aoho 。

点击关注,第一时间了解华为云新鲜技术~

一文带你搞懂从动态代理实现到Spring AOP相关推荐

  1. 一文带你弄懂 Java 动态代理 | 原力计划

    作者 | mjzuo 责编 | 王晓曼 出品 | CSDN 博客 在说动态代理之前,先来简单看下代理模式. 代理是最基本的设计模式之一.它能够插入一个用来替代"实际"对象的&quo ...

  2. RPC框架:一文带你搞懂RPC

    RPC是什么(GPT答) ChatGPT回答: RPC(Remote Procedure Call)是一种分布式应用程序的编程模型,允许程序在不同的计算机上运行.它以一种透明的方式,将一个程序的函数调 ...

  3. 如何查询你电脑的IP地址?一文带你搞懂IP地址

    上一章介绍了数据链路层--以太网数据帧的报文格式(你知道以太网数据帧在网络中如何发送和接收的吗?一文带你搞懂它),本章介绍下网络层--IP地址. 大家都知道计算机都会有一个IP地址,只有配置了IP地址 ...

  4. RPC框架:从原理到选型,一文带你搞懂RPC

    大家好,我是华仔,RPC系列的文章是我去年写的,当时写的比较散,现在重新进行整理.对于想学习RPC框架的同学,通过这篇文章,让你知其然并知其所以然,便于以后技术选型,下面是文章内容目录: RPC 什么 ...

  5. 一文带你搞懂C#多线程的5种写法

    一文带你搞懂C#多线程的5种写法 1.简介 超长警告! 在学习本篇文章前你需要学习的相关知识: 线程基本知识 此篇文章简单总结了C#中主要的多线程实现方法,包括: Thread 线程 ThreadPo ...

  6. ipv6单播地址包括哪两种类型_IPV6中为啥没有ARP了呢?一文带你搞懂NDP邻居发现协议...

    前言 前面我们介绍了ICMPv6协议 除了提供ICMPv4常用的基本功能之外,还有邻居发现(ND)的功能.一文带你看懂ICMPv6和ICMPv4的区别 那么究竟什么是邻居发现协议(ND)呢? 邻居发现 ...

  7. 一文带你搞懂什么是测试开发!

    需要说明的是,原文发表于作者的公众号中,文章篇幅虽长,但内容朴实.且能帮助读者进一步理解测试开发工作,请读者耐心品完~ 01 开始前说点什么 1. 自我反省 公众号开通了也有两年多了,除了刚开通的那段 ...

  8. 带你轻松搞懂JDK动态代理机制(ssm框架先行必备)

    学习动态代理之前需要先搞清楚静态代理,可以回看我之前在Mybatis里写的代理那一篇文章 MyBatis入门基础3 ---代理 动态代理 使用反射机制,在程序执行过程中,创建代理类对象,无需创建类文件 ...

  9. 一文带你搞懂HTTP和RPC协议的异同

    1 什么是RPC协议 1.1 简介 这是老生常谈的协议了,RPC即远程过程调用(Remote Procedure Call), RPC协议是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络 ...

最新文章

  1. MAC Pro 同时安装 Python2 和 Python3
  2. linux批量部署war工具,Linux批量部署工具Expect
  3. MyBatis创建SqlSession-怎么拿到一个SqlSessionTemplate?
  4. 在vscode中用tsc编译ts文件的时候报错,tsc : 无法加载文件,因为在此系统上禁止运行脚本;SecurityError
  5. Java String类型变量的比较问题
  6. (二)Cypher语言常用方法举例
  7. 内核中的内存申请:kmalloc、vmalloc、kzalloc、get_free_pages 之间的区别
  8. proxytable代理不生效_民法典房屋买卖合同卖方代签合同生效吗
  9. 奇安信代码卫士帮助微软修复严重漏洞,获官方致谢和奖金
  10. 一句powershell调用mimikatz抓密码
  11. Ubuntu/Windows 双系统安装教程
  12. 1.2.2-凑零钱问题(暴力递归+动态规划)
  13. 梅特勒托利多电子秤显示EEP服务器错误,托利多电子秤故障排除和维修方法
  14. a++ 和 ++a 的区别
  15. 正大期货:期货交易常用的软件
  16. 验证码——python去除干扰线
  17. 细讲如何判断文法是否是LL(1)文法
  18. 《SteamVR2.2.0官方教程(二)》(Yanlz+Unity+XR+VR+AR+MR+SteamVR+Valve+Tutorials+Interaction+Oculus+立钻哥哥++ok++)
  19. java重复录入怎么删除,删除重复记录的方法
  20. ubuntu:更新源操作步骤

热门文章

  1. 安全整数和 Number.isSafeInteger()
  2. vue 内的 对象 获取 键值对_vue.js - vue 登录存localStorage的方法
  3. mysql case默认_MySQL知识整理10.1—存储过程和函数
  4. ios 获取固件版本_觉得iOS测试版本BUG太多?系统降级试一试?
  5. 计算技术计算机技术属于中图分类法哪个大类,计算技术、计算机技术属于中图分类法哪个大类...
  6. WebSocket转载
  7. animate.css官网
  8. 在CentOS 7 上搭建LAMP
  9. Standard C++ Episode 10
  10. Snake Ladders bfs