聊聊: 在开发中经常会碰到 日志记录、权限检查、事务控制 等问题,如果全都手动进行编码,代码量将会非常夸张,不符合 DRY 原则,也不遵循开闭原则,或者通过封装等方式简化代码,依旧不遵循开闭原则,也为后期维护增加压力。这个时候就可以使用 AOP 的思想了。
概念
AOP 是一种面向切面编程的思想,大概可以理解为,把与业务逻辑无关的操作分离出来。举个例子:把一块面包切成几段,每段之间加了一些好吃夹层,增加了口感又不影响原有的面包分量。
常用AOP框架 AspectJ
//定义切面类
@Aspect
@Component
public class AopTest {
/**
* Pointcut 支持多种表达式来定义切点:
* <p>
* 1. 使用 @annotation: 当方法上添加了指定注解时,切点匹配。
* <br>示例: {@code @annotation(com.example.annotation.TestAnno)}
* <p>
* 2. 使用 execution: 根据方法签名匹配特定的方法。
* <br>注意:第一个星号(*)后需要跟随一个空格。
* <br>示例: {@code execution(* com.example.service.impl.*ServiceImpl.*(..))}
* <p>
* 3. 使用 @within: 当类级别的注解被应用时生效。适用于注解应用于整个类的情况。
* <br>示例: {@code @within(com.example.annotation.TestAnno)}
* <p>
* 每种方式都有其适用场景,根据实际需求选择合适的方式来定义切点。
*/
@Pointcut("@annotation(xx.xx.annotation.TestAnno)")
public void txPointcut() {}
/**
* 前置通知
* @param joinPoint 用来访问当前连接点的静态部分,如方法签名和参数等,但不提供对目标方法执行的控制能力。
*/
@Before("txPointcut()")
public void beforeAdvice(JoinPoint joinPoint) {
System.out.println("Before method: " + joinPoint.getSignature().getName());
}
/**
* 返回之后通知
* @param joinPoint 用来访问当前连接点的静态部分,如方法签名和参数等,但不提供对目标方法执行的控制能力。
* @param result 返回值绑定自定义参数名
*/
@AfterReturning(pointcut = "txPointcut()", returning = "result")
public void afterReturningAdvice(JoinPoint joinPoint, Object result) {
System.out.println("Method: " + joinPoint.getSignature().getName() + " returned with: " + result);
}
/**
* 抛出(异常)后执⾏通知
* @param joinPoint 用来访问当前连接点的静态部分,如方法签名和参数等,但不提供对目标方法执行的控制能力。
* @param ex 发生异常时绑定自定义参数名
*/
@AfterThrowing(pointcut = "txPointcut()", throwing = "ex")
public void afterThrowingAdvice(JoinPoint joinPoint, Throwable ex) {
System.out.println("Method: " + joinPoint.getSignature().getName() + " threw exception: " + ex);
}
/**
* 后置通知
* @param joinPoint 用来访问当前连接点的静态部分,如方法签名和参数等,但不提供对目标方法执行的控制能力。
*/
@After("txPointcut()")
public void afterAdvice(JoinPoint joinPoint) {
System.out.println("After method: " + joinPoint.getSignature().getName());
}
/**
* 围绕通知
* @param joinPoint 继承了 JoinPoint 的所有功能,且提供控制目标方法的执行(即通过调用 proceed() 方法来执行目标方法)。
* @return 目标方法处理后结果
* @throws Throwable 目标方法抛出的异常
*/
@Around("txPointcut()")
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
// 方法执行前的操作
System.out.println("Before method: " + joinPoint.getSignature().getName());
// 业务逻辑...
Object result = joinPoint.proceed();
// 方法执行后的操作
System.out.println("After method: " + joinPoint.getSignature().getName());
return result;
}
}
定义注解
// 语法格式
元注解
修饰符 @interface 注解名 {
// 成员(属性)
返回值类型 方法名() default 默认值
}
1.元注解
-
@Target: 表示注解可以贴在哪些位置(类,方法上,构造器上等等)
-
@Retention: 用于描述注解的生命周期
-
@Documented: 使用 @Documented 标注的标签会保存到 API文档中.
-
@Inherited: @Inherited标注的标签可以被子类所继承.
2.示例代码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy=RUNTIME)
@Inherited
@interface A{ }
@A
class Merchandise{ }
3.返回值类型
4.拓展
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Content {
public String value() default "hello world";
}
import com.wwx.web.util.Content;
import lombok.Data;
@Data
@Content
//商户
public class Merchandise {
//编号
private Integer goodsId;
//名称
private String name;
//数量
private Integer number;
}
@GetMapping("/anno")
public String test(){
Merchandise merchandise = new Merchandise();
Class<? extends Merchandise> merchandiseClass = merchandise.getClass();
Content content = merchandiseClass.getAnnotation(Content.class);
return content.value();
}
注意
1.AOP失效情况
1.Spring Boot 的自动配置将自动启用 AspectJ 自动代理,无需手动设置 @EnableAspectJAutoProxy 。使用 Spring 上下文注入 Bean,AOP 生效
2.当使用自定义 @Configuration 方式来创建 bean 时,AOP不生效,需要手动添加 @EnableAspectJAutoProxy 类注解
@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
//...
}
3.默认情况下,Spring Boot 的自动配置将 Spring AOP 配置为使用 CGLib 代理。若要改用 JDK 代理,请将 spring.aop.proxy-target-class 设置为 false 。
本文介绍了AOP(面向切面编程)的概念,如何通过AspectJ框架实现切面类,以及各种通知类型(如前置、后置、环绕)的应用,以简化日志记录、权限检查等任务,遵循DRY和开闭原则。
422

被折叠的 条评论
为什么被折叠?



