Spring AOP定义
到底什么是AOP,很多小伙伴八股文背的6的一,但是实际业务可能工作两年都没用过。所以来说AOP到底是什么玩意?
AOP 是 Aspect Oriented Programming 的缩写,译为面向切向编程。
图片来理解:
假设我有四个行为:
现在有个需求:
- 在我所有行为之前我都需要 洗手
- 在我所有行为之后我都需要 上厕所
我们应该怎么办:
- 解决办法一:每个方法之前加入 洗手 逻辑, 每个方法之后加入 上厕所 逻辑。
- 解决办法二:抽取出一个公用的 洗手 和 上厕所 方法,然后每个行为之前调用 洗手方法,每个行为之后调用 上厕所方法。
如果我们不允许修改源码呢?
OK不多说了,AOP就可以解决这些问题。
所以AOP其实就是一种思想,一种抽象出 点、面、前置通知、后置通知、环绕通知 的一种思想。
emmmmm,你这不是在难为我嘛,你这是强行为了使用AOP而使用AOP,我不服,公共方法就是比AOP好,而且也很好维护啊!
其实公共方法当然也很好,但是AOP却能使用不同的场景,在我们现在的业务场景复杂多变,公共方法也不能完全适用于所有的场景,这个时候AOP就应运而生了。
Spring AOP 和 Aspectj框架
Spring 框架提供了两种 AOP 的实现方式:Spring AOP 和 AspectJ。这两种方式都可以用于在 Spring 应用中实现切面编程,但它们具有一些不同的特点和使用方式。
-
Spring AOP:
- 基于动态代理技术,通过 JDK 动态代理或 CGLIB 字节码增强来实现 AOP。
- 只支持方法级别的切面,无法对类级别的切面进行处理。
- 只能在 Spring 托管的 Bean 上应用切面,无法对非 Spring Bean 进行切面。
- 只支持基于方法执行的切点表达式,如
execution()
。 - 配置简单,无需额外的编译步骤,只需在 XML 配置文件或注解中定义切面和通知。
- 适用于简单的 AOP 需求和对 Spring 生态系统的集成。
-
AspectJ:
- 是一种独立于 Spring 的 AOP 框架,提供更强大和灵活的 AOP 功能。
- 通过字节码增强(AspectJ 编译器)或运行时动态代理(Spring AOP 支持)来实现 AOP。
- 支持方法级别和类级别的切面,可以对任意的类和方法进行切面处理。
- 可以在任何 Java 项目中使用,不限于 Spring 应用。
- 支持多种切点表达式,如
execution()
、within()
、args()
等。 - 配置较为复杂,需要额外的编译步骤和配置文件,如使用 AspectJ 编译器进行编译和生成 AspectJ 的配置文件。
- 适用于复杂的 AOP 需求和对 AOP 功能的更高级控制。
综上所述,Spring AOP 是 Spring 核心框架自带的 AOP 实现,适用于简单的 AOP 需求和对 Spring 生态系统的集成;而 AspectJ 则是独立的 AOP 框架,提供更强大和灵活的 AOP 功能,适用于复杂的 AOP 需求和对 AOP 功能的更高级控制。JAVA项目中企业中多使用AspectJ。
SpringBoot 集成 Aspectj框架
1、Maven引入
版本使用的就是SpringBoot的版本
java
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2、快速入门
创建一个新的类,作为切面类。切面类是一个普通的Java类,用于定义切面的行为,使用@Aspect
注解进行标记。
java
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.demo.controller.*.*(..))")
public void beforeAdvice() {
System.out.println("Before method execution");
}
}
在Spring Boot的启动类中,使用@EnableAspectJAutoProxy注解来启用AspectJ自动代理。
java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@SpringBootApplication
@EnableAspectJAutoProxy
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
在控制器类中编写一些方法,以便在请求处理过程中触发切面逻辑。
java
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DemoController {
@GetMapping("/")
public String hello() {
return "Hello, World!";
}
}
Aspectj 中常用 注解 解析
1、@Aspect
声明一个类是切面类。
java
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggingAspect {
}
2、@Pointcut
@Pointcut
是AspectJ框架中的注解,用于定义切入点。切入点是在目标方法上执行通知的特定位置。
@Pointcut
注解可以与@Before
、@After
、@Around
等注解一起使用。使用
@Pointcut
注解时,可以指定一个表达式来匹配目标方法。
java
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggingAspect {
@Pointcut("execution(* com.example.demo.controller.*.*(..))")
public void controllerMethods() {}
@Before("controllerMethods()")
public void beforeAdvice() {
System.out.println("Before method execution");
}
}
execution()
@Pointcut("execution(* com.example.demo.controller.*.*(..))")
execution是用于匹配方法执行的切点指示符。它的语法如下:
sql
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
各部分说明:
modifiers-pattern
:可选项,用于指定方法的修饰符。例如,public
表示公共方法,private
表示私有方法,*
表示任意修饰符。ret-type-pattern
:用于指定方法的返回类型。例如,void
表示没有返回值,*
表示任意返回类型。declaring-type-pattern
:可选项,用于指定方法所在的类或接口。可以使用通配符*
来匹配任意类或接口。name-pattern
:用于指定方法的名称。可以使用通配符*
来匹配任意方法名。param-pattern
:用于指定方法的参数类型。可以使用通配符*
来匹配任意参数类型,或使用..
表示任意个数或任意类型的参数。throws-pattern
:可选项,用于指定方法可能抛出的异常类型。可以使用通配符*
来匹配任意异常类型。
例子:
execution(public * *(..))
:匹配所有公共方法。execution(* com.example.demo.controller.*.*(..))
:匹配位于com.example.demo.controller
包下的所有方法。execution(* com.example.demo.controller.UserController.*(..))
:匹配UserController
类中的所有方法。execution(* com.example.demo.controller.UserController.get*(..))
:匹配以get
开头的方法。execution(* com.example.demo.controller.UserController.*(..) throws java.io.IOException)
:匹配抛出java.io.IOException
异常的方法。
@annotation()
是一个切点函数,用于匹配被特定注解标记的方法。使用该切点函数来拦截带有指定注解的方法,并在其执行前后织入通知逻辑。
java
@Aspect
@Component
public class MyAspect {
@Around("@annotation(com.example.demo.annotations.Loggable)")
public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {
// 在目标方法执行前的逻辑
System.out.println("Before method execution");
// 执行目标方法
Object result = joinPoint.proceed();
// 在目标方法执行后的逻辑
System.out.println("After method execution");
return result;
}
}
3、@Before
在目标方法执行之前执行通知。
java
@Aspect
@Component
public class MyAspect {
@Before("execution(* com.example.demo.controller.*.*(..))")
public void beforeMethod(JoinPoint joinPoint) {
// 在目标方法执行之前执行的逻辑
System.out.println("Before method execution");
}
}
4、@After
在目标方法执行之后无论是否发生异常都执行通知。
java
@Aspect
@Component
public class MyAspect {
@After("execution(* com.example.demo.service.*.*(..))")
public void afterMethod(JoinPoint joinPoint) {
// 在目标方法执行之后执行的逻辑
System.out.println("After method execution");
}
}
5、@AfterReturning
在目标方法返回结果后执行通知。
java
@Aspect
@Component
public class MyAspect {
@AfterReturning(pointcut = "execution(* com.example.demo.service.*.*(..))", returning = "result")
public void afterReturningMethod(JoinPoint joinPoint, Object result) {
// 在目标方法成功返回后执行的逻辑
System.out.println("After method returning");
System.out.println("Result: " + result);
}
}
在afterReturningMethod
方法中,可以通过JoinPoint
参数获取目标方法的详细信息,通过returning
参数获取目标方法的返回结果。
6、@AfterThrowing
在目标方法抛出异常后执行通知。
java
@Aspect
@Component
public class MyAspect {
@AfterThrowing(pointcut = "execution(* com.example.demo.service.*.*(..))", throwing = "exception")
public void afterThrowingMethod(JoinPoint joinPoint, Exception exception) {
// 在目标方法抛出异常后执行的逻辑
System.out.println("After method throwing");
System.out.println("Exception: " + exception.getMessage());
}
}
在afterThrowingMethod
方法中,可以通过JoinPoint
参数获取目标方法的详细信息,通过throwing
参数获取目标方法抛出的异常。
7、@Around
在目标方法前后执行通知,可以控制目标方法的执行。
java
@Aspect
@Component
public class MyAspect {
@Around("execution(* com.example.demo.service.*.*(..))")
public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {
// 在目标方法执行前的逻辑
System.out.println("Before method execution");
// 执行目标方法
Object result = joinPoint.proceed();
// 在目标方法执行后的逻辑
System.out.println("After method execution");
return result;
}
}
在aroundMethod
方法中,通过ProceedingJoinPoint
参数调用proceed()
方法来执行目标方法。在proceed()
方法之前的逻辑表示在目标方法执行前执行,proceed()
方法之后的逻辑表示在目标方法执行后执行。
觉得作者写的不错的,值得你们借鉴的话,就请点一个免费的赞吧!这个对我来说真的很重要。