AOP(面向切面编程)
AOP(面向切面编程)

AOP(面向切面编程)

AOP为了将代码中非核心功能代码隔离出来。日志记录,事务管理,性能监测等功能通常分散在多个类中,导致代码重复,分散。这些非核心功能模块被称为横切关注点,而AOP允许将这些关注点集中到一块(切面)。相当于包装在一起,自动植入到相关代码中。

横切关注点:

就是与核心业务代码无关的,但是又贯穿多个模块的功能,例如:

  • 日志记录:可以自动在每个方法调用前后记录日志。
  • 事务管理:可以在数据库操作时,自动开启,提交,回滚事务。
  • 性能测试:可以在方法执行前后记录监控方法的执行性能。

切面:

切面就是横切关注点的模块化实现,可以包换多个“通知”(Advice)和“切入点”(Pointcut),定义了在哪里实现,如何实现横切逻辑。根据发送的时间,分为:

  • 前置通知(Before Advice):在方法调用前执行。
  • 后置通知(After Advice):在方法调用后执行。
  • 返回通知(After Returning Advice):在方法成功返回后执行。
  • 异常通知(After Throwing Advice):在方法抛出异常后执行。
  • 环绕通知(Around Advice):在方法执行的前后都执行,可以完全控制方法的执行过程。

切入点

在Spring AOP中,切入点(Pointcut)是用来定义拦截哪些方法的。

连接点:

业务逻辑层中可以插入横切关注点的位置都叫做连接点。

织入:

织入是将切面和目标对象结合的过程。织入一般发送在运行时,通过动态代理来完成。

AOP的具体实现

  //pom.xml中添加依赖
            <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>6.2.11</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjtools</artifactId>
            <version>1.9.7</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.7</version>
        </dependency>

切面定义:用”@Aspect”来注解这个类是个切面,再用”@Component”,把这个类交给Spring管理。

切入点的定义:规定需要插入增强的方法。

@Pointcut("execution(* org.cy.store.service.impl.UserServiceImpl.login(String,String))")
    public void loginPointcut() {}
//execution后面加上切入点的路径以及方法,如果是同一个类下的很多方法,可以用.UserServiceImpl.*(..)这种方式标记。
//加上这段话之后,后面所有对这些方法的使用都可以用loginPointcut()代替。
@Before("loginPointcut()")
    public void beforeLogin(JoinPoint joinPoint) {
        System.out.println("beforeLogin");
    }

切入点表达式的定义规则

execution表达式:

语法格式:(  [修饰符模式]  返回类型  [类全路径] . 方法名(参数列表)  [异常模式]  )

  • 修饰符模式(可选):匹配方法的访问修饰符,如public,private,如果不指定,匹配所有修饰符。
  • 返回类型:匹配方法的返回类型,通配符 * 表示任意返回类型。
  • 类全路径(可选):可以是类的全路径(如com.example.UserService),也可以是通配符 * 表示包含的子类。
  • 方法名:匹配具体的方法名或使用通配符 * 表示任意方法。
  • 参数列表:匹配方法的参数,可以是具体的类型(int,String),也可以是通配符 (我个人认为,这里是为了重载方法时,即使方法名不一样,也可以确定到某个具体的方法)。
  • 异常模式(可选):匹配抛出的异常类型。

within表达式:

匹配特定类或包中的所有方法,常用于指定切入点的作用范围。无法确定到具体的某个函数,最多到类

args表达式:

匹配方法的参数类型,与execution()的参数部分类似,但是args()可以在运行时获取参数的实际裂隙,而不是编译时确定的类型。

this表达式:

匹配当前代理对象的类型,用于匹配代理类。

target表达式:

匹配目标对象的类型,它和this()类似,但是target()匹配的是目标对象的类型,而不是代理对象。

目标对象的匹配刚好和代理对象反过来,代理对象的匹配是遇到接口的实现类,回头去找接口类,而目标对象的匹配则是遇到接口类,去找该接口类的实现类。

  • this() 通常填写接口类型,因为代理对象的类型是接口类型(尤其是在使用 JDK 动态代理时)。
  • target() 通常填写实现类类型,因为目标对象的实际类型是实现类,而不管代理对象是什么类型。

@annotation():

匹配所有类上带有特定注解的方法。

@within():

匹配所有带有特定标记的类中的方法。

bean():

匹配Bean的名称

方法中的参数

JoinPoint的常用方法

方法说明
getArgs()返回目标方法的参数,是一个Object[]数组。
getSignature()获取目标方法的签名(包括方法名,返回类型,参数类型等信息)
getTarget()获取目标对象
getThis()获取代理对象
getString()返回当前连接点的字符串表示。

ProceedingJoinPoint 的常用方法(继承与JoinPoint ,用于@Around通知)

方法说明
proceed()返回目标方法的参数,是一个Object[]数组。
proceed(Object[] args)获取目标方法的签名(包括方法名,返回类型,参数类型等信息)
getArgs()返回目标方法的参数,是一个Object[]数组。
getSignature()获取目标方法的签名(包括方法名,返回类型,参数类型等信息)
getTarget()获取目标对象
getThis()获取代理对象

温馨提示(很重要)

由于AOP底层是通过生成代理对象来实施拦截,调用路径要经过代理对象,AOP增强逻辑才能实现。如果直接在内部调用另一个方法,就不会代理对象,而是调用自己本身的方法,所有不会触发AOP拦截。

上面段话简单来说,就是接口里面有的方法可以被拦截到,而实现接口的类里面新加的方法不会被拦截。只要在启动类或者AOP配置类上加上下面这段代码,Spring会强制使用CGLIB代理,就可以拦截到了。

@EnableAspectJAutoProxy(proxyTargetClass = true)

final类不能被代理,final方法不能被拦截

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注