Spring-AOP

AOP

基于动态代理实现AOP,spring aop代理必须使用代理对象才能生效

概念

概念说明
切⾯(aspect)按关注点进⾏模块分解时,横切关注点就表⽰为⼀个切⾯
连接点(join point)程序执⾏的某⼀刻,在这个点上可以添加额外的动作
通知(advice)切⾯在特定连接点上执⾏的动作
切⼊点(pointcut)切⼊点是⽤来描述连接点的,它决定了当前代码与连接点是否匹配

JDK 动态代理与 CGLIB 代理的区别

必须要实现接⼝⽀持拦截 public ⽅法⽀持拦截 protected ⽅法拦截默认作⽤域⽅法
JDK 动态代理
CGLIB 代理

基于 @Aspect 的配置

  • 引入依赖,以便使⽤Aspect 相关的注解和功能。
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
  • 要开启 @Aspect ⽀持,可以在 Java 配置类上增加@EnableAspectJAutoProxy 注解
    • @EnableAspectJAutoProxy 有两个属性,proxyTargetClass ⽤于选择是否开
      启基于类的代理(是否使⽤ CGLIB 来做代理);exposeProxy ⽤于选择是否将代
      理对象暴露到 AopContext 中,两者默认值都是 false
    • 在 Spring Boot 中,会⾃动配置使⽤基于类的代理
@Configuration
@EnableAspectJAutoProxy
public class Config {...}

声明切入点

  • @Pointcut中的⼀些常⽤ PCD
PCD说明
execution最常⽤的⼀个 PCD,⽤来匹配特定⽅法的执⾏
within匹配特定范围内的类型,可以⽤通配符来匹配某个 Java 包内的所有类
thisSpring AOP 代理对象这个 Bean 本⾝要匹配某个给定的类型
target⽬标对象要匹配某个给定的类型,⽐ this 更常⽤⼀些
args传⼊的⽅法参数要匹配某个给定的类型,它也可以⽤于绑定请求参数
beanSpring AOP 特有的⼀个 PCD,匹配 Bean 的 ID 或名称,可以⽤通配符
@target执⾏的⽬标对象带有特定类型注解
@args传⼊的⽅法参数带有特定类型注解
@annotation拦截的⽅法上带有特定类型注解
  • 切⼊点表达式⽀持与、或、⾮运算,运算符分别为 &&、||和 !

  • execution ⽤得⾮常多,下⾯详细描述⼀下它的表达式,[] 代表可选项,<>
    代表必选项:execution([修饰符] <返回类型> [全限定类名.]<⽅法>(<参数>) [异常])

    • 每个部分都可以使⽤*通配符
    • 类名中使⽤.*表⽰包中的所有类,..* 表⽰当前包与⼦包中的所有类
    • 参数主要分为以下⼏种情况:
      • () 表⽰⽅法⽆参数
      • (…) 表⽰有任意个参数
      • (*) 表⽰有⼀个任意类型的参数
      • (String) 表⽰有⼀个 String 类型的参数
      • (String,String) 代表有两个 String 类型的参数
package learning.spring.helloworld;
public class HelloPointcut {
@Pointcut("target(learning.spring.helloworld.Hello)")
public void helloType() {} // ⽬标对象是learning.spring.helloworld.Hello类型
@Pointcut("execution(public * say())")
public void sayOperation() {} // 执⾏public的say()⽅法
@Pointcut("helloType() && sayOperation()") // 复⽤其他切⼊点
public void sayHello() {} // 执⾏Hello类型中public的say()⽅法
}




// learning.spring.helloworld及其⼦包中所有类⾥的say⽅法
// 该⽅法可以返回任意类型,第⼀个参数必须是String,后⾯可以跟任意参数
execution(* learning.spring.helloworld..*.say(String,..))
// learning.spring.helloworld及其⼦包
within(learning.spring.helloworld..*)
// ⽅法的参数仅有⼀个String
args(java.lang.String)
// ⽬标类型为Hello及其⼦类
target(learning.spring.helloworld.Hello+)
// 类上带有@AopNeeded注解
@target(learning.spring.helloworld.AopNeeded)

由于 Spring AOP 的实现基于动态代理,因⽽只能匹配普通⽅法的执⾏,像静态
初始化、静态⽅法、构造⽅法、属性赋值等操作都是拦截不到的

声明通知

import com.example.dto.ParamVo;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;


@Component
@Aspect
public class AspectDemo {
    @Pointcut("execution(public * demo(..))")
    public void helloType() {}

    /**
     * 前置通知的⽅法没有返回值,因为它在被拦截的⽅法前执⾏,
     * 就算有返回值也没地⽅使⽤,
     * 但是它可以对被拦截⽅法的参数进⾏加⼯,通过 args 这个 PCD 能
     * 明确参数,并将其绑定到前置通知⽅法的参数上。
     */
    @Before("helloType() && args(vo)")
    public void before(ParamVo vo) {
        System.out.println("Before Advice");
    }

    /**
     * 添加了 @After 注解的⽅法必须要能够处理正常与异常这两种情况,
     * 但它⼜获取不到返回值或异常对象,所以⼀般只⽤来做⼀些资源清理的⼯作
     */
    @After("helloType()")
    public void after() {
        System.out.println("After Advice");
    }

    /**
     *  ⽅法的参数 result 就是被拦截⽅法的返回值,⽽且此处限定了
     * 该通知只拦截返回值是 String 类型的调⽤。
     * 需要提醒的是,returning 中给定的名字必须与⽅法的参数名保持⼀致。
     * @param result
     */
    @AfterReturning(pointcut = "helloType()",returning = "result")
    public void afterReturning(String result) {
        System.out.println("AfterReturning Advice");
    }

    /**
     * 想要拦截抛出异常的调⽤
     * @param exception
     */
    @AfterThrowing(pointcut = "helloType()",throwing = "exception")
    public void afterThrowing(Exception exception) {
        System.out.println("AfterThrowing Advice");
    }

    /**
     * 第⼀个参数必须是 ProceedingJoinPoint 类型的,
     * ⽅法的返回类型是被拦截⽅法的返回类型,或者直接⽤ Object 类型。
     * @param pjp
     */
    @Around("helloType()")
    public Object around(ProceedingJoinPoint pjp) {
        System.out.println("Around Advice");
        try {
            //其中的 pjp.proceed() 就是调⽤具体的连接点进⾏的处理,proceed() ⽅法,也接受 Ojbect[] 参数,可以替代原先的参数。
            return pjp.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }
}
  • 同时存在多个通知作⽤于同⼀处,可以让切⾯类实现 Ordered 接⼝,或者在上⾯添加 @Order 注解。指定的值越低,优先级则越⾼,在最终的代理对象执⾏时也会先执⾏优先级⾼的逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值