Spring AOP知识点详解

Spring AOP是 Spring最核心的能力,那到底什么是AOP呢,今天了不起带大家了解一下。

AOP是什么

AOP(Aspect Oriented Programming):面向切面编程,是OOP(面向对象编程)的一个延续,其和OOP一样,也是一种编程思想,不过AOP是一种横向开发模式。

OOP ,面向对象,允许开发者定义纵向的关系,但并适用于定义横向的关系,导致了大量代码的重复,而不利于各个模块的重用。
AOP ,一般称为面向切面,作为面向对象的一种补充,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为"切面 "(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于权限认证.日志.事务处理。
AOP实现的关键在于代理模式 ,AOP代理主要分为静态代理动态代理 。静态代理的代表为AspectJ动态代理则以Spring AOP为代表。

  1. AspectJ是静态代理的增强,所谓静态代理,就是AOP框架会在编译阶段生成AOP代理类,因此也称为编译时增强,他会在编译阶段将AspectJ(切面)织入到Java字节码中,运行的时候就是增强之后的AOP对象。
  2. Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。

Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理:

  1. JDK动态代理只提供接口代理,不支持类代理,核心InvocationHandler接口和Proxy类,InvocationHandler 通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起,Proxy利用 InvocationHandler动态创建一个符合某一接口的的实例, 生成目标类的代理对象。
  2. 如果代理类没有实现 InvocationHandler 接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
  3. 静态代理与动态代理区别在于生成AOP代理对象的时机不同,相对来说AspectJ的静态代理方式具有更好的性能,但是AspectJ需要特定的编译器进行处理,而Spring AOP则无需特定的编译器处理。
    InvocationHandler invoke(Object proxy,Method method,Object[] args)proxy是最终生成的代理实例; method 是被代理目标实例的某个具体方法;args 是被代理目标实例某个方法的具体入参, 在方法反射调用时使用。

AOP使用场景

AOP的主要作用就是减少代码量,提高代码的可重用性,有利于未来的可操作性与可维护性。

主要操作就是将所有模块中共同拥有的代码,单独抽取出来,放在一块地方,在主代码运行之前或之后,或主程序运行的其他时间点执行这块代码。也可以理解成把这些单独抽出来的代码封装成一个个单独的方法,但是这些方法的执行不需要我们在程序中进行显示地调用,而是通过动态代理的方式来帮助我们执行这些方法。AOP的主要应用场景是一些相似性代码比较高的场景,比如:权限认证,日志,事务等。

AOP术语

  1. 连接点(Joinepoint):是程序执行的某个特定位置,如类开始初始化前、类初始化后、类的某个方法调用前/后、方法抛出异常后。一个类或者一段程序代码拥有一些具有边界性质的特定点,这些特定点就称为 "连接点" 。这边有个注意的点就是spring的链接点只支持方法,也就是只能是方法在调用前,调用后,抛出异常后。
  2. 切点(Pointcut): 切点就是能够定位到特定连接点的点。也就是能够通过切点知道程序在哪个连接点之前或之后执行。
  3. 增强(Advice): 增强说白了就是你想要在切点上执行的代码逻辑,也就是在连接点之前或之后执行的那段代码。
  4. 目标对象(Target): 就是你要插入代码的类,也就是连接点所在的类。
  5. 引介(Introduction): 是一种特殊的增强,为类添加一些属性和方法。
  6. 织入(Weaving):是一个过程,就是将增强添加到目标类具体连接点上的过程。
  7. 代理(Proxy): 就是融合了目标类和增强逻辑后生成的那个对象
  8. 切面(Aspect): 由切点和增强组成,包含了切点的定义与增强的代码逻辑的定义

AOP使用入门样例

  1. 引入依赖
powershell 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
  1. 定义一个Controller作为测试
powershell 复制代码
public class AopController {

    @RequestMapping("/hello")
    public String sayHello(){
        System.out.println("hello");
        return "hello";
    }
}
  1. 定义切面类

Spring采用@AspectJ注解对POJO进行标注,该注解表明该类不仅仅是一个POJO,还是一个切面。切面是切点和通知的结合,那么定义一个切面就需要编写切点和通知。

@Pointcut注解可以在一个切面内定义可重用的切点。

Spring切面粒度最小是达到方法级别,execution表达式可以用于明确指定方法返回类型,类名,方法名和参数名等与方法相关的部件,并且实际开发中,大部分需要使用AOP的业务场景也只需要达到方法级别即可,因而execution表达式的使用是最为广泛的。

powershell 复制代码
@Aspect
@Component
public class AopAdvice {

    @Pointcut("execution (* com.xxx.aop.controller.*.*(..))")
    public void test() {

    }
}

切点通过@Pointcut注解已经定义好,那接下来就需要定义通知。
AOP通知有五种类型,分别是

前置通知(@Before):在目标方法调用之前调用通知

后置通知(@After):在目标方法完成之后调用通知

环绕通知(@Around):在被通知的方法调用之前和调用之后执行自定义的方法

返回通知(@AfterReturning):在目标方法成功执行之后调用通知

异常通知(@AfterThrowing):在目标方法抛出异常之后调用通知

代码中定义了三种类型的通知,使用@Before注解标识前置通知,打印"beforeAdvice...",使用@After注解标识后置通知,打印"AfterAdvice...",使用@Around注解标识环绕通知,在方法执行前和执行之后分别打印"before"和"after"。

powershell 复制代码
@Before("test()")
public void beforeAdvice() {
    System.out.println("beforeAdvice...");
}

@After("test()")
public void afterAdvice() {
    System.out.println("afterAdvice...");
}

@Around("test()")
public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) {
    System.out.println("before");
    try {
        proceedingJoinPoint.proceed();
    } catch (Throwable t) {
        t.printStackTrace();
    }
    System.out.println("after");
}
  1. 结果

运行AopApplication,在浏览器访问http://localhost:8080/hello,不出意外,控制台输出如图所示:

AOP总结

纸上得来终觉浅,绝知此事要躬行,大家实践一下,也就知道具体的AOP是什么了,只要实践后,一切都变容易了。

相关推荐
吾日三省吾码4 小时前
JVM 性能调优
java
Estar.Lee4 小时前
查手机号归属地免费API接口教程
android·网络·后端·网络协议·tcp/ip·oneapi
弗拉唐5 小时前
springBoot,mp,ssm整合案例
java·spring boot·mybatis
oi776 小时前
使用itextpdf进行pdf模版填充中文文本时部分字不显示问题
java·服务器
2401_857610036 小时前
SpringBoot社团管理:安全与维护
spring boot·后端·安全
少说多做3436 小时前
Android 不同情况下使用 runOnUiThread
android·java
知兀6 小时前
Java的方法、基本和引用数据类型
java·笔记·黑马程序员
蓝黑20206 小时前
IntelliJ IDEA常用快捷键
java·ide·intellij-idea
Ysjt | 深6 小时前
C++多线程编程入门教程(优质版)
java·开发语言·jvm·c++