我们进入ruoyi-framework,立刻看到的内容
了解一下aspectj 这个概念
概念
面向切面编程(AOP)
面向切面编程(AOP)是一种编程范式,重点聚焦于软件应用程序中的关注点分离。AOP 背后的思想是软件应用程序具有多个切面,其中一些切面跨越了模块化编码的典型划分,就是可能多个模块化代码都会使用,导致代码分散和混乱。
在软件开发中,有些功能包括日志记录、安全检查、错误处理和数据验证。在面向对象编程 (OOP) 等传统编程范式中,这些横切关注点通常与核心业务逻辑纠缠在一起,导致代码难以维护和扩展。
AOP(Aspect Oriented Programming)面向切面编程的概念比较抽象,主要涉及下面这些术语:
- Aspect(切面):切入一个或多个类的模块
- Join point(连接点):程序执行的节点,例如执行方法或处理异常
- Advice(通知):切面在连接点执行的动作,例如前置通知
- Pointcut(切点):用于匹配连接点,一般通过表达式匹配
- Target object(目标对象):被切入的对象,可以被一个或多个切面切入
一个目标类可以被多个切面切入,多个切面也可以切入一个目标类。
切面实现的本质是一个或多个基于连接点的拦截器。
AOP的运行
AOP提供了一种分离关注点的新维度,AOP会将这些关注点模块成称为“切面”的单独单元,这些切面可以独立开发、测试和重用。然后,将它们在需要的地点“编织”到主代码库中,确保核心逻辑保持不变且连贯。这种编织可以在不同的时间发生:
- 编译时:编译应用程序时会编织各个切面。
- 加载时:加载应用程序类时会编织各个切面。
- 运行时:各个切面是在应用程序执行期间编织的。
AOP的好处
- OOP非常适合使用类和对象对现实世界的对象和行为进行建模。但是,当涉及到跨模块间的的功能,OOP就显得不够用了。例如:如果你想在OOP结构中实现记录日志,你可能会在多个类中添加日志记录代码,这就显得非常冗余,随着时间的推移,这些重复的代码会扰乱主要逻辑,导致阅读、修改、调试变得非常困难。
- 除了模块化之外,AOP还提供了高度的灵活性。由于切面与主要业务逻辑分离,因此对它们的更改不会影响核心代码。例如,如果更改日志记录的实现方式(也许切换到不同的日志库),则可以修改日志记录切面,而无需修改代码的任何其他部分。这种架构极大地简化了维护、减少了错误并提高了代码清晰度。
Spring AOP VS Full AspectJ
- 值得注意的是,虽然 Spring AOP 涵盖了许多常见用例,但它并没有提供 AspectJ 等成熟 AOP 框架提供的所有功能。 Spring AOP 专注于通过代理进行运行时编织,而 AspectJ 可以在编译时或加载时编织切面,提供更广泛的连接点(如字段访问)。
- AspectJ 是一个独立于 Spring 的,功能强大的 AOP 框架。它通过编译时植入(CTW, Compile-Time Weaving)或者运行时植入(LTW, Load-Time Weaving)来实现切面逻辑的织入。与 Spring AOP 相比,AspectJ 提供了更多的切面功能和更高的性能。Spring AOP 和 AspectJ 可以结合使用,利用 AspectJ 的强大功能和 Spring AOP 的简便配置。
- 使用 AspectJ,需要在 Java 代码中创建切面类,并使用 @Aspect、@Pointcut、@Before、@After、@Around 等注解来定义切面和切点。编译时需要使用 AspectJ 编译器(ajc)编译代码,或者在运行时使用 Load-Time Weaving 实现切面逻辑的植入。
AOP原理
- Spring AOP 的核心 Spring AOP 机制的核心是代理。当您定义一个切面来应用于应用程序的某些部分时,Spring 会创建advice对象的代理(子类或接口实现)。该代理拦截调用并将它们委托给原始对象,这种动态代理方法可确保不存在字节码操作,从而使过程透明且不易出错。Spring AOP 底层是基于动态代理实现的,对实现接口的类进行代理,默认使用 JDK 动态代理,对没实现接口的类,使用 CGLIB 动态代理。
JDK 动态代理通过实现接口生成代理类,使用拦截器加反射机制生成。
CGLIB 动态代理通过继承生成代理子类,使用字节码技术生成。通过子类对父类的方法进行重写来实现代理,因此,对 final 修饰的方法不能代理。
动态代理
JDK提供了invocationHandler接口和Proxy类,借助这两个工具可以达到我们想要的效果。 如果想更深入的的了解动态原理的实现,可以参照 Java 动态代理作用是什么? - 知乎
例子
若依的操作日志部分,注解此类 @Aspect ,简单粗暴,然后重写几个方法就可以
@Aspect
@Component
public class LogAspect
{
看几个方法,主要是 操作日志进行处理 SysOperLog 这是操作记录表,继承了BaseEntity,持久化到数据库里
- 使用@Before在切入点开始处切入内容
- 使用@AfterReturning在切入点return内容之后切入内容(可以用来对处理返回值做一些加工处理)
- 使用@AfterThrowing用来处理当切入内容部分抛出异常之后的处理逻辑
@Before(value = "@annotation(controllerLog)")
public void boBefore(JoinPoint joinPoint, Log controllerLog)
{
TIME_THREADLOCAL.set(System.currentTimeMillis());
}
/**
* 处理完请求后执行
*
* @param joinPoint 切点
*/
@AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult)
{
handleLog(joinPoint, controllerLog, null, jsonResult);
}
/**
* 拦截异常操作
*
* @param joinPoint 切点
* @param e 异常
*/
@AfterThrowing(value = "@annotation(controllerLog)", throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e)
{
handleLog(joinPoint, controllerLog, e, null);
}
可以推测,在有注解@Log 的Controller(可以随便找几个,完成测试) 的方法里面,在其前面, 在执行完成以后,或者发生异常的时候,我们都在这个方法上做了日志记录,很容易理解 切面的好处,以及是如何使用的。
总结
面向切面编程(AOP)是软件开发领域的游戏规则改变者。 @Aspect 和 @Pointcut 注解等工具,实现 AOP 变得更加简化,使开发人员能够专注于应用程序的核心逻辑,同时单独管理横切点。如果使用得当,这些注解可以带来更清晰、更易于维护和更高效的代码结构。