Spring AOP 应用

Spring AOP 应用

1. 介绍

AOP:面向切面编程,对面向对象编程的一种补充。

AOP可以将一些公用的代码,自然的嵌入到指定方法的指定位置。

比如:

如上图,我们现在有四个方法,我们想在每个方法执行一开始,输出一个日志信息。但是这样做很麻烦,如果有100个、1000个方法,工作量会很大,而且难以维护。这时候就可以通过AOP进行解决。


2. 案例实战

2.1 需求分析及环境搭建

环境:SpringBoot + SpringBoot Web + SpringBoot AOP。

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

目标:控制器业务方法,统一进行日志输出。

新建User类,包含idname属性。

新建UserController

java 复制代码
@RestController
@RequestMapping("/user")
public class UserController {
    @GetMapping("/list")
    public List<User> list(){
        return Arrays.asList(
                new User(1,"张三"),
                new User(2,"李四"),
                new User(3,"王五")
        );
    }
    @GetMapping("/getById/{id}")
    public User getById(@PathVariable("id") Integer id){
        return new User(id,"张三");
    }
    @GetMapping("/deleteById/{id}")
    public boolean deleteById(@PathVariable("id") Integer id){
        return true;
    }
}

此时,我们的目标就是使用AOP的方式,给这个listdeleteByIdgetById方法加上日志。

日志要包括调用方法的名称、返回值以及参数列表。

2.3 AOP实现

1. 首先我们要让AOP知道哪些方法需要被AOP处理 -> 通过注解方式进行处理

java 复制代码
// 定义一个注解,来标记需要添加日志的方法
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogAnnotation {
    String value() default "";
}

定义好注解后,给需要使用日志的方法添加注解,如:

java 复制代码
@LogAnnotation("查询用户")   // 标记目标方法
@GetMapping("/getById/{id}")
public User getById(@PathVariable("id") Integer id){
    return new User(id,"张三");
}

2. 实现切面任务

新建LogAspect类,这就是生成切面对象的类。我们需要用@Component注解进行标注,交给IOC容器进行管理。此外,我们要用@Aspect注解标注其为一个切面。

然后,我们要将这个切面与我们刚刚标注的@LogAnnotation注解建立联系,让切面知道从哪个位置进行切入。实现的方法为,新建一个方法,然后给这个方法添加@Pointcut("@annotation(自定义注解的全类名)")。这样就成功建立的联系。

确定切入点后,我们就可以写切面的实际任务了。新建一个方法around。此时,我们要将确定切点的方法与切面实际处理任务的方法进行关联。实现的方法为,给实际处理任务的方法添加@Around("标记切点的方法名()")注解。

此时,我们只有一个around方法,要用这一个方法对listgetByIddeleteById三个方法进行处理。那么around方法如何分辨这三个不同的方法呢?这时就需要用到一个连接点对象ProceedingJoinPointaround的返回值为object类型,其要返回所切入方法的返回值。

然后,就可以实现日志输出功能了。

java 复制代码
@Aspect
@Component
@Slf4j
public class LogAspect {
    @Pointcut("@annotation(cn.codewei.aopstudy.annotation.LogAnnotation)")
    public void logPointCut() {
    }
    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        // 方法名称
        String name = point.getSignature().getName();
        // 通过反射 获取注解中的内容
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        LogAnnotation annotation = method.getAnnotation(LogAnnotation.class);
        String value = annotation.value();
        // 输出日志
        log.info("方法名称:{}, 方法描述: {}, 返回值: {}, 参数列表: {}", name, value, point.proceed(), point.getArgs());
        // 返回切入方法的返回值
        return point.proceed();
    }
}

@Around、@Before、@After区别

  • @Before前置通知,是在所拦截方法执行之前执行一段逻辑,返回值类型为void。
  • @After 后置通知,是在所拦截方法执行之后执行一段逻辑,返回值类型为void。
  • @Around 环绕通知,是可以同时在所拦截方法的前后执行一段逻辑,用这个注解的方法入参传的是ProceedingJoinPoint,返回结果类型为Object,返回结果为ProceedingJoinPoint对象的.proceed();

3. @Pointcut

  • 使用within表达式匹配

​ 匹配com.leo.controller包下所有的类的方法

java 复制代码
@Pointcut("within(com.leo.controller..*)")
public void pointcutWithin(){
}
  • this匹配目标指定的方法,此处就是HelloController的方法
java 复制代码
@Pointcut("this(com.leo.controller.HelloController)")
public void pointcutThis(){
}
  • target匹配实现UserInfoService接口的目标对象
java 复制代码
@Pointcut("target(com.leo.service.UserInfoService)")
public void pointcutTarge(){
}
  • bean匹配所有以Service结尾的bean里面的方法

    注意:使用自动注入的时候默认实现类首字母小写为bean的id

java 复制代码
@Pointcut("bean(*ServiceImpl)")
public void pointcutBean(){
}
  • args匹配第一个入参是String类型的方法
java 复制代码
@Pointcut("args(String, ..)")
public void pointcutArgs(){
}
  • @annotation匹配是@Controller类型的方法
java 复制代码
@Pointcut("@annotation(org.springframework.stereotype.Controller)")
public void pointcutAnnocation(){
}
  • @within匹配@Controller注解下的方法,要求注解的@Controller级别为@Retention(RetentionPolicy.CLASS)
java 复制代码
@Pointcut("@within(org.springframework.stereotype.Controller)")
public void pointcutWithinAnno(){
}
  • @target匹配的是@Controller的类下面的方法,要求注解的@Controller级别为@Retention(RetentionPolicy.RUNTIME)
java 复制代码
@Pointcut("@target(org.springframework.stereotype.Controller)")
public void pointcutTargetAnno(){
}
  • @args匹配参数中标注为@Sevice的注解的方法
java 复制代码
@Pointcut("@args(org.springframework.stereotype.Service)")
public void pointcutArgsAnno(){
}
  • 使用excution表达式
java 复制代码
@Pointcut(value = "execution(public * com.leo.controller.HelloController.hello*(..))")
public void pointCut() {
}
相关推荐
麦兜*9 分钟前
Spring Boot启动优化7板斧(延迟初始化、组件扫描精准打击、JVM参数调优):砍掉70%启动时间的魔鬼实践
java·jvm·spring boot·后端·spring·spring cloud·系统架构
KK溜了溜了27 分钟前
JAVA-springboot 整合Redis
java·spring boot·redis
天河归来37 分钟前
使用idea创建springboot单体项目
java·spring boot·intellij-idea
weixin_478689761 小时前
十大排序算法汇总
java·算法·排序算法
码荼1 小时前
学习开发之hashmap
java·python·学习·哈希算法·个人开发·小白学开发·不花钱不花时间crud
IT_10241 小时前
Spring Boot项目开发实战销售管理系统——数据库设计!
java·开发语言·数据库·spring boot·后端·oracle
ye902 小时前
银河麒麟V10服务器版 + openGuass + JDK +Tomcat
java·开发语言·tomcat
武昌库里写JAVA2 小时前
Oracle如何使用序列 Oracle序列使用教程
java·开发语言·spring boot·学习·课程设计
做题不NG3 小时前
大模型应用开发-LangChain4j
java
今天背单词了吗9803 小时前
算法学习笔记:7.Dijkstra 算法——从原理到实战,涵盖 LeetCode 与考研 408 例题
java·开发语言·数据结构·笔记·算法