type
status
date
slug
summary
tags
category
icon
password
ext
order
comment
前言
首先,什么是AOP? AOP(Aspect-Oriented Programming:面向切面编程) 能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如日志记录,性能统计,权限控制,事务处理,异常处理等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。
AOP和OOP很像,一字母之差,但是两者却是面向不同领域的两种思想。OOP(面向对象)主要是针对业务处理过程中的实体、属性、行为进行抽象封装,OOP的三大特性,封装、继承、多态。
AOP是面向切面,切入的哪?切入的是业务处理过程中的某个步骤或阶段,比如一个用户User类,其有多个行为,你想在这些行为做之前判断这个用户是否有权限,或者记录下他这些行为发生的时间,耗时等相关信息,而又不想在每个行为中都加入这些判断权限,记录日志的相关代码,显得重复代码特别多, 这个时候利用AOP就可以很好的切入,只需要一个AOP的实现类,你就可以切入到这些行为,无论是开始时的位置,还是结束时的位置。
关于Spring AOP
Spring AOP就是基于动态代理的,如果要代理的对象,实现了某个接口,那么Spring AOP会使用JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候Spring AOP会使用Cglib ,这时候Spring AOP会使用 Cglib 生成一个被代理对象的子类来作为代理 ,如下图所示
还有AspectJ,Spring AOP已经集成了AspectJ, AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了,后面讲的AOP的使用就是基于AspectJ的注解方式的切面 , Spring AOP 属于运行时增强,而 AspectJ 是编译时增强。 Spring AOP 基于代理(Proxying),而 AspectJ 基于字节码操作(Bytecode Manipulation),所以性能必然是AspectJ更好一些,当然,切面较少时无所谓了。
使用AOP时,真实的情形是,当应用调用被AOP代理的方法时,AOP代理会在自己的方法中回调目标对象的方法(下方代码中的joinPoint.proceed();),从而完成这个被代理方法的调用,而且joinPoint.proceed(); 前后就可以任意根据自己需求进行相关代码添加,比如日志,事务,权限等等。
AOP的实践
代码再最下方,这个代码作用是记录控制器方法的执行时间
说下里面的一些含义
- join point(连接点):是程序执行中的一个精确执行点,例如类中的一个方法,我们通过joinPoint.proceed()即可执行这个方法
- point cut(切入点):本质上是一个捕获连接点的结构,代表切入到那些方法内,比如com.example.controller.*.*() 第一个代表某个类,第二个()则代表某个方法
- @Before 前置通知,方法执行前触发
- @AfterReturning 后置返回通知,在@Around中执行结束返回结果时触发
- @After 后置通知,方法执行后出发
- @Around 环绕通知, 最重要的一个, joinPoint.proceed() 即在此内执行
- @AfterThrowing 异常通知,顾名思义,发生异常时触发
Spring 定义切入点语法
除了ret-type-pattern(返回类型模式}、name-pattern(param-pattern}(名字模式和参数模式}外,其他模式都是可选的。返回类型模式决定了方法的返回类型,必须依次匹配一个连接点(即一个方法}。使用最频繁的一个返回类型模式是*,它代表了匹配任意的返回类型,如果写明了返回类型,比如String,那么只能匹配返回String类型的连接点(方法}。名字模式匹配的是方法名,你可以用通配符*表示匹配所有方法名。参数模式中,( )表示匹配了无参数的方法,而(..)表示匹配任意数量参数的方法。模式(*)表示匹配任意类型参数的方法。模式(*,String)表示匹配:第一个为任意参数类型,第二个必须为String类型的方法。
modifiers-pattern:方法的操作权限ret-type-pattern:返回值declaring-type-pattern:方法所在的包name-pattern:方法名parm-pattern:参数名throws-pattern:异常
下面是定义切入点的例子:
任意公共方法的执行:excution(public * (..))任何一个以set开头的方法执行:excution( set(..))AccountService接口的任意方法的执行:excution( com.example.service.AccountService.(..))定义在service包的任意方法的执行:excution( com.example.service..(..))定义在service包或子包的任意方法的执行:excution(* com.example.service...(..))
引用一张比较直观的图
执行效果
- 作者:Loneking
- 链接:https://loneking.cn/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/84
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。