欢迎关注我的 Github,如果觉得有帮助,请点个 star
哟,目前主要在更 leetcode题解(Java版)
和 剑指offer题解(Java版)
,可以点个star
。
文本已收录至我的GitHub仓库,欢迎Star:awesome-java-notes
在学习 Spring AOP 之前,我们先来了解一下 AOP。我们都听过面向对象编程(OOP),那么 AOP 到底是什么呢?中文意思我想大家应该都是非常的耳熟能详了,中文翻译过来就是面向切面编程
,当然也有些人会翻译成面向方面编程,不过还是感觉面向切面编程更好听点。来看下官方的定义:
AOP is a programming paradigm that aims to increase modularity by allowing the separation of cross-cutting concerns.
翻译过来就是,AOP 是一种编程范式,旨在通过分离横切关注点来增加模块化。它通过向现有代码添加其他行为而不修改现有代码本身来实现。相反,我们可以分别声明这个新代码和这些新行为。
【注】面向对象编程的基本单元是类(class)
,而面向切面编程的基本单元是切面(aspect)
。
看完了上面 AOP 的定义,那么接下来我们来看下什么是 Spring AOP 呢?
Spring AOP 在 Spring 应用程序实现了面向切面编程。在 AOP 中,这些切面实现了一些关注点的模块化,诸如事务管理
,日志记录
或跨越多种类型和对象的安全性
(通常称为横切关注点)。
大多数的技术都会形成自己的一套术语,Spring AOP 也不例外。而 Spring AOP 最主要的三个术语分别是通知(Advice)
、切点(Pointcut)
和连接点(Join point)
。如图,展示了这三者是如何联系到一块的。
建言(Advice) 通知定义了切面是什么以及何时使用,是在方法执行之前或之后要采取的实际操作。Spring 的面向切面编程框架在程序执行期间调用的时间代码段。
切点(Pointcut) 切点是一个与连接点相匹配的表达式,用于确定是否需要执行建言(Advice)。切点有助于确定切面所通知的连接点的范围,它的定义会匹配通知所要织入的一个或多个连接点。切点使用与连接点匹配的不同类型的表达式,在 Spring 框架中使用 Aspect 切点表达式语言(SpEL)。
连接点(Join Point) 连接点在应用程序中是具体的点,例如方法执行,异常处理,改变对象的变量值等。在 Spring AOP 中一个连接点总是一个方法的执行点,即只有方法执行点。
目标对象
引入新的接口和对应的实现。织入(weaving)
可以在目标对象的几个不同的生命周期执行:
【樱木天亥注】AspectJ 织入编译器属于编译期织入;AspectJ 5 的装载期织入(LTW,load-time weaving)属于类装载期织入;Spring AOP 织入切面则属于运行期织入。
1、AOP 是非侵入性的
2、AOP 是通过纯 Java 语言实现的
3、通过 Spring IOC 容器实现依赖注入
4、能够将多个横切关注点织入类中而无需调用这些类的横切关注点
5、能够集中或模块化横切关注点,使得维持和改变这些切点变得容易维护
6、提供了 XML 或 @AspectJ 注解的多种灵活的方式来创建切面
7、易于配置
1、使用基于代理的 AOP,所以仅支持方法级别的建言,不支持属性级别的建言
2、当且仅当方法的可见性为 public 时,才支持建言(Advice)
3、一个切面不能作为另一个切面的 advice target 。
目标对象
了, 因为使用 @Aspect 后, 这个类就会被排除在 auto-proxying 机制之外。4、由于性能问题,建言不适用于细粒度的对象,只适合粗粒度的对象。
建言(Advice)的类型有一下五种。
我们都知道,Spring AOP 底层主要有两种实现,一种是 JDK 动态代理,一种是 CGLIB 动态代理。而 Spring AOP 代理的实现是通过 JDK 动态代理来创建一个具有目标类和通知调用的 Proxy 类,这些类被称为 AOP 代理类。
那么,JDK 动态代理和 CGLIB 动态代理最大不同是什么呢?前者的原理是 JDK 反射,并且只支持 Java 接口的代理;后者的原理是继承(extend)与覆写(override),因此能支持普通的 Java 类的代理。两种方式都是动态代理,即运行时实时生成代理。但是,由于JVM 的限制,CGLIB 无法替换被代理类已经被载入的字节码,只能生成并载入一个新的子类作为代理类,被代理类的字节码依然存在于JVM 中。
两种声明方式:
1、XML 方式
<!-- 使用 XML 方式,启用 AspectJ 风格的 Spring AOP -->
<aop:aspectj-autoproxy />
2、Java 配置的方式
//使用 Java 配置的方式,启用 AspectJ 风格的 Spring AOP
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
}
定义切面(Aspect)
@Component
@Aspect
public class EmployeeAspect {
@Before("execution(* EmployeeManager.getEmployeeById(..))") //point-cut expression
public void logBefore(JoinPoint joinPoint) {
……
}
}
【樱木天亥注】仅仅使用 @Aspect 注解, 并不能将一个 Java 对象转换为 Bean, 因此我们还需要使用类似 @Component 之类的注解。
切点表达式
@Before("execution(* EmployeeManager.getEmployeeById(..))") //point-cut expression
public void logBeforeAdvice(JoinPoint joinPoint) {
……
}
【樱木天亥注】
1、execution(* EmployeeManager.getEmployeeById(…)) 这个切点表达式表示这个切点将会匹配 EmployeeManager 类下面所有的 getEmployeeById 方法,而不管它的参数类型和数量是什么。
2、@Before 表示当前的 Advice 是在连接点(Join point)之前执行。
连接点(Join point)
//@Component
public class EmployeeManager
{
public EmployeeDTO getEmployeeById(Integer employeeId) {
System.out.println("Method getEmployeeById() called");
return new EmployeeDTO();
}
}
【樱木天亥注】如果前面定义切面(Aspect)时没有添加 @Component 注解,则这里应该要加上,不然可能不能将一个 Java 对象转换为 Bean。
声明建言(Advice)
Advice 与切点表达式紧密关联,会在匹配的连接点执行前,执行后或者周围(around)执行。切点表达式既可以是简单的一个 pointcut 名字的引用, 又可以是完整的 pointcut 表达式。这里简单的举几个例子说明如下。
Around advice
@Around("execution(* EmployeeManager.getEmployeeById(..))")
public Object logAroundAdvice(ProceedingJoinPoint proceedingJoinPoint){
……
}
Before advice
@Before("execution(* EmployeeManager.getEmployeeById(..))") //point-cut expression
public void logBeforeAdvice(JoinPoint joinPoint) {
……
}
如果你同我一样想要征服数据结构与算法、想要刷 LeetCode,欢迎关注我 GitHub 上的 LeetCode 题解:awesome-java-notes
参考来源
1、Introduction to Spring AOP
2、Spring AOP Tutorial Example
3、Spring AOP Example Tutorial – Aspect, Advice, Pointcut, JoinPoint, Annotations, XML Configuration
4、Overview of Spring Aspect Oriented Programming (AOP)
5、Spring AOP 是什么?
6、彻底征服 Spring AOP 之 理论篇
7、《Spring实战(第 4 版)》
欢迎扫码关注我的公众号「蜗牛永动机」,回复 1024 免费获取精心整理的编程学习资源~