SpringAOP详细解读

目录

一.SpringAOP是什么

1.1理论

1.2主要功能

1.2.1分离关注点

1.2.2增强模块化

1.2.3动态代理

1.3例子

二.SpringAOP核心概念

2.1切点(Pointcut)

 2.2通知(Advice)

2.3切面优先级

 2.4切点表达式书写

2.4.1execution()表达式

2.4.2@annotation表达式


一.SpringAOP是什么

1.1理论

Spring Aop(Aspect-Oriented Programming,面向切面编程)是一个Spring框架中的一个核心模块,主要用于处理横切关注点。

1.2主要功能

1.2.1分离关注点

提供一种对所有目标对象的通用行为进行集中管理的方式。

如日志记录,事物管理,权限控制,性能审计等。

1.2.2增强模块化

通过创建可复用的切面,可以使应用程序的业务逻辑更具模块化。

1.2.3动态代理

Spring AOP使用动态代理技术在运行时实现切面逻辑而不是在编译时,因此可无侵入地添加功能。

1.3例子

引入依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

 定义切面类

@Aspect
@Component
public class MyAspect {
    @Pointcut("execution(* com.example.test.controller..*.*(..))")
    public void log(){
    }
    @Before("log()")
    public void beforeController() {
        System.out.println("before controller");
    }
    @After("log()")
    public void afterController1() {
        System.out.println("after controller1");
    }
    @Around("log()")
    public Object aroundController1(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("around start controller1");
        Object proceed = joinPoint.proceed();
        System.out.println("around end controller1");
        return proceed;
    }
    @AfterThrowing("log()")
    public void throwController1() {
        System.out.println("throw controller1");
    }
    @AfterReturning("log()")
    public void returnController1() {
        System.out.println("return controller1");
    }
}

 设计测试用的controller类

@RestController
@RequestMapping("/test")
public class ControllerTest {
    @MyAspect1
    @RequestMapping("/t1")
    public String test() {
        return "test";
    }
    @RequestMapping("/t2")
    public String test2() {
        throw new RuntimeException("failed");
    }

}

二.SpringAOP核心概念

我们先进行分析上一段代码

@Aspect:切面类,告诉Spring,这个类是一个切面类

@Pointcut:切点,告诉Spring要针对那些方法进行切入

@Before、@Around、@AfterReturning、@After、@AfterThrowing:通知,告诉Spring针对后要做什么处理

2.1切点(Pointcut)

在Spring AOP中,Pointcut(切点)是用于定义特定的连接点(Join Points),即横切关注点应该应用到的地方。Spring通过切点表达式来指定匹配特定的类、方法、参数等,让切面能够在运行时准确地找到需要织入的点。

 

这是使用切点的情况下,如果不适用切点的话会使代码变的非常冗余

 @Before("execution(* com.example.test.controller..*.*(..))")
    public void beforeController() {
        System.out.println("before controller");
    }
    @After("execution(* com.example.test.controller..*.*(..))")
    public void afterController1() {
        System.out.println("after controller1");
    }

这是不适用切点的情况下,相当于每一种通知都会加上具体的目标地址,使用了切点,我们只需要加上定义切点的方法即可。

 2.2通知(Advice)

Spring中AOP的通知类型有以下⼏种:
@Around: 环绕通知, 此注解标注的通知⽅法在⽬标⽅法前, 后都被执⾏
@Before: 前置通知, 此注解标注的通知⽅法在⽬标⽅法前被执⾏
@After: 后置通知, 此注解标注的通知⽅法在⽬标⽅法后被执⾏, ⽆论是否有异常都会执⾏
@AfterReturning: 返回后通知, 此注解标注的通知⽅法在⽬标⽅法后被执⾏, 有异常不会执⾏
@AfterThrowing: 异常后通知, 此注解标注的通知⽅法发⽣异常后执⾏

我们这里主要看环绕通知,返回后通知,异常后通知 

 @Around("log()")
    public Object aroundController1(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("around start controller1");
        Object proceed = joinPoint.proceed();
        System.out.println("around end controller1");
        return proceed;
    }
    @AfterThrowing("log()")
    public void throwController1() {
        System.out.println("throw controller1");
    }
    @AfterReturning("log()")
    public void returnController1() {
        System.out.println("return controller1");
    }

 

可见,我们定义了三种通知类型,但是只是有两种通知类型,因此,异常通知类型是当异常发生后才会进行通知。

我们进行修改代码

@RestController
@RequestMapping("/test")
public class ControllerTest {
    @MyAspect1
    @RequestMapping("/t1")
    public String test() {
        int a=1/0;
        return "test";
    }
    @RequestMapping("/t2")
    public String test2() {
        throw new RuntimeException("failed");
    }

}

运行结果

 

发生了异常,但是最后环绕通知也没有完全生成。当我们进行捕获了异常,环绕通知才会完整的生成。 

 

这是有异常后,各个通知类型是否执行以及执行的顺序。

2.3切面优先级

当我们定义了多个切面类的时候,并且这几个切面类同时匹配到了同一个方法时候,那他们的执行顺序是什么样呢

这里我创建了两个切面类,来进行测试,但是这里只是进行展示了前置通知和后置通知。

@Aspect
@Component
public class MyAspect11 {
    @Pointcut("execution(* com.example.test.controller..*.*(..))")
    public void log(){
    }
    @Before("execution(* com.example.test.controller..*.*(..))")
    public void beforeController() {
        System.out.println("后置通知类11");
    }
    @After("execution(* com.example.test.controller..*.*(..))")
    public void afterController1() {
        System.out.println("后置通知类11");
    }

}

我们先看不定义@order注解看看顺序是什么样的?

通过对比,可以发现,存在多个切面类的时候,按照切面类的类名字母排序

  •  @Before 通知:字⺟排名靠前的先执⾏
  •  @After 通知:字⺟排名靠前的后执⾏

如果我们想要让某一个具体切面类先执行的时候,怎么办

答:Spring提供了一个全新的注解,来控制这些切面通知的执行顺序:@Order

我们在Aspect11,Aspect12分别加上@Order(1),@Order(2)

通过上述程序的运⾏结果, 得出结论:
@Order 注解标识的切⾯类, 执⾏顺序如下:
@Before 通知:数字越⼩先执⾏
@After 通知:数字越⼤先执⾏
@Order 控制切⾯的优先级, 先执⾏优先级较⾼的切⾯, 再执⾏优先级较低的切⾯, 最终执⾏⽬标⽅法

 2.4切点表达式书写

2.4.1execution()表达式

execution()是最常用的切点表达式,用来匹配方法。

 execution(<访问修饰符> <返回类型> <包名.类名.⽅法(⽅法参数)> <异常>)

 1.*匹配任意字符,只匹配一个参数(返回类型,包,类名,方法或者是方法参数)。

 2.  ..匹配多个连续的任意符号,可以匹配任意级的包,或者是任意类型,任意个数的参数,

如果包下嵌套有包,可以使用这种办法。

2.4.2@annotation表达式

这个表达式主要是用来自定义注解的时候使用的,当我们要匹配两个不同类的一个方法,此时就可以使用这种方式

自定义注解

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface MyAspect1 {
    
}

 注解解释:

一.@Target 标识了 Annotation 所修饰的对象范围, 即该注解可以⽤在什么地⽅. 常⽤取值:
ElementType.TYPE: ⽤于描述类、接⼝(包括注解类型) 或enum声明
ElementType.METHOD: 描述⽅法
ElementType.PARAMETER: 描述参数
ElementType.TYPE_USE: 可以标注任意类型
二. @Retention 指Annotation被保留的时间⻓短, 标明注解的⽣命周期,@Retention 的取值有三种:
RetentionPolicy.SOURCE:表⽰注解仅存在于源代码中, 编译成字节码后会被丢弃. 这意味着在运⾏时⽆法获取到该注解的信息, 只能在编译时使⽤. ⽐如 @SuppressWarnings , 以及 lombok提供的注解 @Data , @Slf4j
RetentionPolicy.CLASS:编译时注解. 表⽰注解存在于源代码和字节码中, 但在运⾏时会被丢弃. 这意味着在编译时和字节码中可以通过反射获取到该注解的信息, 但在实际运⾏时⽆法获 取. 通常⽤于⼀些框架和⼯具的注解.
RetentionPolicy.RUNTIME:运⾏时注解. 表⽰注解存在于源代码, 字节码和运⾏时中. 这意味着在编译时, 字节码中和实际运⾏时都可以通过反射获取到该注解的信息. 通常⽤于⼀些需要 在运⾏时处理的注解, 如Spring的 @Controller @ResponseBody

看一下切面类是如何进行书写的

@Aspect
@Component
public class MyAspect2 {
    @Before("@annotation(com.example.test.aspect.MyAspect1)")
    public void beforeController() {
        System.out.println("before controller");
    }
}

 我们使用execution表达式的时候,通常里面写的是匹配方法的规则,现在写的是自定义注解的路径。这一点跟之前不同

如何在测试方法中进行使用呢

@RestController
@RequestMapping("/test")
public class ControllerTest {
    @MyAspect1
    @RequestMapping("/t1")
    public String test() {
        System.out.println("test");
        return "test";
    }
    @RequestMapping("/t2")
    public String test2() {
        throw new RuntimeException("failed");
    }
}

在相应的方法上加上刚才自定义的注解。

总结:

    Spring AOP(Aspect-Oriented Programming,面向切面编程)是 Spring 框架中的一个模块,用于实现横切关注点的模块化开发。代理是 Spring AOP 实现的一种方式。

Spring AOP 实现代理的方式有两种:

基于 JDK 动态代理: 如果目标对象实现了至少一个接口,Spring AOP 就会使用 JDK 动态代理来为目标对象创建代理。在运行时,Spring AOP 会动态生成一个实现了目标对象所有接口的代理对象,并在代理对象的方法中织入切面逻辑。

基于 CGLIB 代理: 如果目标对象没有实现任何接口,Spring AOP 就会使用 CGLIB(Code Generation Library)来为目标对象创建代理。CGLIB 使用字节码生成技术,在运行时生成目标对象的子类,并重写其中的方法来织入切面逻辑。

与其说是Spring AOP是一种方法,不如说是一种思想,是一种不改变原来的代码上,对代码达到增强的一种效果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值