AOP(面向切面编程)

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: AOP(面向切面编程)

一.什么是AOP

AOP 为 Aspect Oriented Programming 的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP 是 OOP(面向对象编程)的延续,是软件开发中的一个热点,也是 Spring 框架中的一个重要内容,是函数式编程的一种衍生范型。利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,使用AOP进行编程,可以降低代码的侵入性,提高程序的可重用性,同时提高了开发的效率。

简单来说,aop是一种是一种思想和和规范,通过选择不同方法(可以在不同类)在不同时机(方法执行前、执行后、返回前后、抛出异常后...),对选择的方法统一添加处理逻辑。

springAOP是对AOP思想的一种具体实现,但是它只实现了对方法的增强,没有实现对属性的增强

二.AOP的典型应用场景

  • 统一的日志记录
  • 统一的方法执行时间统计
  • 统一的返回格式设置
  • 统一的异常处理
  • 事务的开启和提交

三.关于AOP的核心知识点

切面类:定义该类是一个切面类

切点(PointCut):定义要增强的方法

Pointcut 的作用就是提供一组规则(使用 AspectJ pointcut expression language 来描述)来匹配 Join Point,给满足规则的 Join Point 添加 Advice

通知:定义方法增强的实现逻辑定义了切面是什么,何时使用,其描述了切面要完成的工作,还解决何时执行这个工作的问题。

切点表达式说明

AspectJ 支持三种通配符:

* :匹配任意字符,只匹配一个元素(包,类,或方法,方法参数)。

* .. :匹配任意字符,可以匹配多个元素 ,在表示类时,必须和 * 联合使用。

+ :表示按照类型匹配指定类的所有类,必须跟在类名后面,如 com.cad.Car+ , 表示继承该类的所有子

类包括本身。

切点表达式由切点函数组成,其中 execution() 是最常用的切点函数,用来匹配方法,语法为:

execution(< 修饰符 >< 返回类型 >< 包 . 类 . 方法 ( 参数 )>< 异常 >)

修饰符 和 异常 可以省略。

表达式示例

5c3ef274a5af4f3c8e5dc13b72dfb72e.png

定义相关通知

通知定义的是被拦截的方法具体要执行的业务,比如用户登录权限验证方法就是具体要执行的业务。Spring AOP 中,可以在方法上使用以下注解,会设置方法为通知方法,在满足条件后会通知本方法进行调用:

前置通知使用@Before:通知方法会在目标方法调用之前执行。

后置通知使用@After:通知方法会在目标方法返回或者抛出异常后调用。

返回之后通知使用@AfterReturning:通知方法会在目标方法返回后调用。

抛异常后通知使用@AfterThrowing:通知方法会在目标方法抛出异常后调用。

环绕通知使用@Around:通知包裹了被通知的方法,在被通知的方法通知之前和调用之后执行自定义 的行为。

四.AOP的实现原理

Spring AOP 是构建在 动态代理 基础上,因此 Spring 对 AOP 的支持局限于方法级别的拦截 。

Spring AOP 支持 JDK Proxy 和 CGLIB 方式实现动态代理。默认情况下,实现了接口的类(被代理类),使用 AOP 会基于 JDK 生成代理类,没有实现接口的类,会基于 CGLIB 生成代理类。

cf2106e3e7e7458e954abc091c5f9830.png

对于使用JDK实现的动态代理:代理类和被代理类要实现同一个接口,在实现的过程中会使用到的

api如下:Invocationhandler,proxy.newProxyInstance

对于基于GCLIB实现的动态代理:继承原始类(原始类不能被final修饰),是专门生成代理类的第三方框架,是基于asm字节码框架生成的

JDK 和 CGLIB 的区别

1. JDK 实现,要求被代理类必须实现接口,之后是通过 InvocationHandler 及 Proxy,在运行时动态的在 内存中生成了代理类对象,该代理对象是通过实现同样的接口实现(类似静态代理接口实现的方式), 只是该代理类是在运行期时,动态的织入统一的业务逻辑字节码来完成。

2. CGLIB 实现,被代理类可以不实现接口,是通过继承被代理类,在运行时动态的生成代理类对象。

织入( Weaving ):代理的生成时机

织入是把切面应用到目标对象并创建新的代理对象的过程,切面在指定的连接点被织入到目标对象中。

在目标对象的生命周期里有多个点可以进行织入:

编译期: 切面在目标类编译时被织入。这种方式需要特殊的编译器。 AspectJ 的织入编译器就是以这种

方式织入切面的。

类加载器: 切面在目标类加载到 JVM 时被织入。这种方式需要特殊的类加载器( ClassLoader ) , 它可以 在目标类被引入应用之前增强该目标类的字节码。AspectJ5 的加载时织入( load-timeweaving. LTW )

就支持以这种方式织入切面。

运行期: 切面在应用运行的某一时刻被织入。一般情况下,在织入切面时, AOP 容器会为目标对象动态创建一个代理对象。SpringAOP 就是以这种方式织入切面的。

五.AOP的实现方式

JDK和GCLIB实现的代理类如下:

JDK

import org.example.demo.service.AliPayService;

import org.example.demo.service.PayService;

 

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

 

//动态代理:使用JDK提供的api(InvocationHandler、Proxy实现),此种方式实现,要求被代理类必须实现接口

public class PayServiceJDKInvocationHandler implements InvocationHandler {

   //目标对象即就是被代理对象

   private Object target;

 

   public PayServiceJDKInvocationHandler(Object target) {

       this.target = target;

   }

 

   //proxy代理对象

   @Override

   public Object invoke(Object proxy, Method method, Object[] args) throws

           Throwable {

//1.安全检查

       System.out.println("安全检查");

//2.记录日志

       System.out.println("记录日志");

//3.时间统计开始

       System.out.println("记录开始时间");

//通过反射调用被代理类的方法

       Object retVal = method.invoke(target, args);

//4.时间统计结束

       System.out.println("记录结束时间");

       return retVal;

   }

 

   public static void main(String[] args) {

       PayService target = new AliPayService();

//方法调用处理器

       InvocationHandler handler =

               new PayServiceJDKInvocationHandler(target);

//创建一个代理类:通过被代理类、被代理实现的接口、方法调用处理器来创建

       PayService proxy = (PayService) Proxy.newProxyInstance(

               target.getClass().getClassLoader(),

               new Class[]{PayService.class},

               handler

       );

       proxy.pay();

   }

}

GCLIB

import org.springframework.cglib.proxy.Enhancer;

import org.springframework.cglib.proxy.MethodInterceptor;

import org.springframework.cglib.proxy.MethodProxy;

import org.example.demo.service.AliPayService;

import org.example.demo.service.PayService;

 

import java.lang.reflect.Method;

 

public class PayServiceCGLIBInterceptor implements MethodInterceptor {

   //被代理对象

   private Object target;

 

   public PayServiceCGLIBInterceptor(Object target) {

       this.target = target;

   }

 

   @Override

   public Object intercept(Object o, Method method, Object[] args, MethodProxy

           methodProxy) throws Throwable {

//1.安全检查

       System.out.println("安全检查");

//2.记录日志

       System.out.println("记录日志");

//3.时间统计开始

       System.out.println("记录开始时间");

//通过cglib的代理方法调用

       Object retVal = methodProxy.invoke(target, args);

//4.时间统计结束

       System.out.println("记录结束时间");

       return retVal;

   }

 

   public static void main(String[] args) {

       PayService target = new AliPayService();

       PayService proxy = (PayService) Enhancer.create(target.getClass(), new

               PayServiceCGLIBInterceptor(target));

       proxy.pay();

   }

}

六.AOP的使用

SpringAOP在使用时主要有以下两种使用方式:

①使用aspectj风格的注解进行开发:

添加依赖

<dependency>

           <groupId>org.springframework.boot</groupId>

           <artifactId>spring-boot-starter-aop</artifactId>

       </dependency>

定义通知时机和通知逻辑

import org.aspectj.lang.ProceedingJoinPoint;

import org.aspectj.lang.annotation.Around;

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Pointcut;

import org.springframework.stereotype.Component;

 

/**

* @author tongchen

* @create 2023-05-08 11:14

*/

@Aspect

//将配置加载到容器中

@Component

public class AopConfig {

   //定义切点

   //包后面一定要有一个空格

   @Pointcut("execution(* com.ljl..service.*Service.*(..))")

   public void pointCut(){

 

   }

   @Around("pointCut()")

   public Object around(ProceedingJoinPoint joinPoint){

 

       //pointcut匹配的方法

       try {

           Long start=System.currentTimeMillis();

           Object proceed = joinPoint.proceed();

           Long end=System.currentTimeMillis();

           System.out.println("方法运行的时间:"+(end-start));

       } catch (Throwable e) {

           throw new RuntimeException(e);

       }

       return null;

   }

 

}

定义切点方法

import org.springframework.stereotype.Service;

 

import java.util.ArrayList;

 

/**

* @author tongchen

* @create 2023-05-08 11:13

*/

@Service

public class AspectService {

   public Object timeTest(){

       //模拟获取数据库中的数据

       return new ArrayList<Integer>();

   }

}

②定义一个类,实现MethodInteceptor

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
4月前
Micronaut AOP与代理机制:实现应用功能增强,无需侵入式编程的秘诀
AOP(面向切面编程)能够帮助我们在不修改现有代码的前提下,为应用程序添加新的功能或行为。Micronaut框架中的AOP模块通过动态代理机制实现了这一目标。AOP将横切关注点(如日志记录、事务管理等)从业务逻辑中分离出来,提高模块化程度。在Micronaut中,带有特定注解的类会在启动时生成代理对象,在运行时拦截方法调用并执行额外逻辑。例如,可以通过创建切面类并在目标类上添加注解来记录方法调用信息,从而在不侵入原有代码的情况下增强应用功能,提高代码的可维护性和可扩展性。
92 1
|
2月前
|
安全 Java 编译器
什么是AOP面向切面编程?怎么简单理解?
本文介绍了面向切面编程(AOP)的基本概念和原理,解释了如何通过分离横切关注点(如日志、事务管理等)来增强代码的模块化和可维护性。AOP的核心概念包括切面、连接点、切入点、通知和织入。文章还提供了一个使用Spring AOP的简单示例,展示了如何定义和应用切面。
253 1
什么是AOP面向切面编程?怎么简单理解?
|
2月前
|
XML Java 开发者
论面向方面的编程技术及其应用(AOP)
【11月更文挑战第2天】随着软件系统的规模和复杂度不断增加,传统的面向过程编程和面向对象编程(OOP)在应对横切关注点(如日志记录、事务管理、安全性检查等)时显得力不从心。面向方面的编程(Aspect-Oriented Programming,简称AOP)作为一种新的编程范式,通过将横切关注点与业务逻辑分离,提高了代码的可维护性、可重用性和可读性。本文首先概述了AOP的基本概念和技术原理,然后结合一个实际项目,详细阐述了在项目实践中使用AOP技术开发的具体步骤,最后分析了使用AOP的原因、开发过程中存在的问题及所使用的技术带来的实际应用效果。
77 5
|
4月前
Micronaut AOP与代理机制:实现应用功能增强,无需侵入式编程的秘诀
【9月更文挑战第9天】AOP(面向切面编程)通过分离横切关注点提高模块化程度,如日志记录、事务管理等。Micronaut AOP基于动态代理机制,在应用启动时为带有特定注解的类生成代理对象,实现在运行时拦截方法调用并执行额外逻辑。通过简单示例展示了如何在不修改 `CalculatorService` 类的情况下记录 `add` 方法的参数和结果,仅需添加 `@Loggable` 注解即可。这不仅提高了代码的可维护性和可扩展性,还降低了引入新错误的风险。
55 13
|
3月前
|
Java 容器
AOP面向切面编程
AOP面向切面编程
53 0
|
5月前
|
XML Java 数据格式
Spring5入门到实战------11、使用XML方式实现AOP切面编程。具体代码+讲解
这篇文章是Spring5框架的AOP切面编程教程,通过XML配置方式,详细讲解了如何创建被增强类和增强类,如何在Spring配置文件中定义切入点和切面,以及如何将增强逻辑应用到具体方法上。文章通过具体的代码示例和测试结果,展示了使用XML配置实现AOP的过程,并强调了虽然注解开发更为便捷,但掌握XML配置也是非常重要的。
Spring5入门到实战------11、使用XML方式实现AOP切面编程。具体代码+讲解
|
5月前
|
Java Spring XML
掌握面向切面编程的秘密武器:Spring AOP 让你的代码优雅转身,横切关注点再也不是难题!
【8月更文挑战第31天】面向切面编程(AOP)通过切面封装横切关注点,如日志记录、事务管理等,使业务逻辑更清晰。Spring AOP提供强大工具,无需在业务代码中硬编码这些功能。本文将深入探讨Spring AOP的概念、工作原理及实际应用,展示如何通过基于注解的配置创建切面,优化代码结构并提高可维护性。通过示例说明如何定义切面类、通知方法及其应用时机,实现方法调用前后的日志记录,展示AOP在分离关注点和添加新功能方面的优势。
77 0
|
6月前
|
Java Spring 容器
Spring问题之Spring AOP是如何实现面向切面编程的
Spring问题之Spring AOP是如何实现面向切面编程的
|
5月前
|
监控 安全 数据库
面向方面编程(AOP)的概念
【8月更文挑战第22天】
92 0
|
5月前
|
XML Java 数据库
Spring5入门到实战------10、操作术语解释--Aspectj注解开发实例。AOP切面编程的实际应用
这篇文章是Spring5框架的实战教程,详细解释了AOP的关键术语,包括连接点、切入点、通知、切面,并展示了如何使用AspectJ注解来开发AOP实例,包括切入点表达式的编写、增强方法的配置、代理对象的创建和优先级设置,以及如何通过注解方式实现完全的AOP配置。