个人技术博客——Springboot中AOP的使用

222000322许影杰 学生 2023-06-07 03:14:58
这个作业属于哪个课程2023年福大-软件工程实践-W班
这个作业要求在哪里软件工程实践寒假作业软件工程实践总结&个人技术博客
这个作业的目标课程回顾与总结、个人技术总结
其他参考文献《构建之法》

目录

  • 一、技术概述
  • 二、技术详述
  • 2.1 AOP流程图
  • 2.2 Maven依赖
  • 2.3 常用术语
  • 2.4 常用注解
  • 2.4.1 @PointCut注解详解
  • 2.5 JoinPoint类详解
  • 2.6 AOP实战应用——接口日志输出
  • 2.6.1 注解类
  • 2.6.2 切面类
  • 三、技术使用中遇到的问题和解决过程
  • 3.1 遇到的问题
  • 3.2 解决过程
  • 四、总结
  • 五、参考文献


一、技术概述

  Spring Boot AOP是一种面向切面编程的技术,使用AOP可以更轻松地实现横切关注点的重用和集中管理,提高代码的可重用性和可测试性。同时提高应用程序的可扩展性和可维护性。其难点在于理解AOP的概念、学习切面和通知的编写方式,并正确应用于实际场景中。

二、技术详述

2.1 AOP流程图

在这里插入图片描述

2.2 Maven依赖

<!-- 引入aop支持 -->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2.3 常用术语

  • 切面:非核心业务功能就被定义为切面。比如一个系统的日志功能,它贯穿整个核心业务的逻辑,因此叫做切面。
  • 切入点:在哪些类的哪些方法上面切入
  • 通知:在方法执行前/后或者执行前后做什么
    • 前置通知:在被代理方法之前执行
    • 后置通知:在被代理方法之后执行
    • 返回通知:被代理方法正常返回之后执行
    • 异常通知:被代理方法抛出异常时执行
    • 环绕通知:是AOP中强大、灵活的通知,集成前置和后置通知
  • 切面:在什么时机、什么地方做什么(切入点+通知)
  • 织入:把切面加入对象,是生成代理对象并将切面放入到流程中的过程(简而言之,就是把切面逻辑加入到核心业务逻辑的过程)

    2.4 常用注解

  • @Pointcut 定义切点
  • @Before 前置通知
  • @After 后置通知
  • @AfterReturning 返回通知
  • @AfterThrowing 异常通知
  • @Around 环绕通知

2.4.1 @PointCut注解详解

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Pointcut {
    String value() default "";

    String argNames() default "";
}

切入点表达式包括一个execution表达式和argNames

例如:

@Pointcut("execution(* edu.fzu.service.*.*(..))")
public void pointCut() {}

2.5 JoinPoint类详解

JoinPoint可获取的信息

//返回目标对象,即被代理的对象
Object getTarget();

//返回切入点的参数
Object[] getArgs();

//返回切入点的Signature
Signature getSignature();

//返回切入的类型,比如method-call,field-get等等
String getKind();

2.6 AOP实战应用——接口日志输出

  通过自定义注解实现对特定方法使用切面

2.6.1 注解类

1.businessName字段:用于表示接口业务描述信息。
2.@Target:表示该注解可以声明在哪些目标元素之前,ElementType.METHOD表明该注解只能声明在一个类的方法前。
3.@Retention:注解类的生命周期。RetentionPolicy.RUNTIME 表示该注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在。

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface SystemLog {
    String businessName();
}

2.6.2 切面类

1.在类上使用@Component注解。
2.在类上使用@Aspect注解 使之成为切面类。
3.@Pointcut:指哪些方法需要被执行"AOP"。
4.@annotation:带有相应注解的任意方法。
5.@Around:环绕通知。
6.ProceedingJoinPoint类: 继承了 JoinPoint。是在JoinPoint的基础上暴露出 proceed 这个方法,仅可用于@Around中。
7.joinPoint.proceed方法:执行被环绕的方法,其返回值就是被环绕方法的返回值。
8.handleBefore方法:在joinPoint.proceed方法执行前即被环绕的方法执行前执行的方法,输出请求的信息日志,与handleAfter方法共同实现“Around”效果。
9.handleAfter方法:在joinPoint.proceed方法执行后即被环绕的方法执行后执行的方法,输出方法返回,与handleBefore方法共同实现“Around”效果。

@Component
@Aspect
@Slf4j
public class LogAspect {

    @Pointcut("@annotation(team.machine.machineguardbe.common.annotation.SystemLog)")
    public void pt() {

    }

    //定义通知方法
    @Around("pt()")
    public Object printLog(ProceedingJoinPoint joinPoint) throws Throwable {
        Object ret;
        try {
            handleBefore(joinPoint);
            ret = joinPoint.proceed();
            handleAfter(ret);
        } finally {
            // 结束后换行System.lineSeparator()(系统的换行符)
            log.info("=======End=======" + System.lineSeparator());
        }
        return ret;
    }

    private void handleAfter(Object ret) {
        // 打印出参
        log.info("Response       : {}", JSON.toJSONString(ret));
    }

    private void handleBefore(ProceedingJoinPoint joinPoint) {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();


        //获取被增强方法上的注释对象
        SystemLog systemLog = getSystemLog(joinPoint);

        log.info("=======Start=======");
        // 打印请求 URL
        log.info("URL            : {}", request.getRequestURL());
        // 打印描述信息
        log.info("BusinessName   : {}", systemLog.businessName());
        // 打印 Http method
        log.info("HTTP Method    : {}", request.getMethod());
        // 打印调用 controller 的全路径以及执行方法
        log.info("Class Method   : {}.{}", joinPoint.getSignature().getDeclaringTypeName(), ((MethodSignature) joinPoint.getSignature()).getName());
        // 打印请求的 IP
        log.info("IP             : {}", request.getRemoteHost());
        // 打印请求入参
        log.info("Request Args   : {}", JSON.toJSONString(joinPoint.getArgs()));
    }

    private SystemLog getSystemLog(ProceedingJoinPoint joinPoint) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        SystemLog systemLog = methodSignature.getMethod().getAnnotation(SystemLog.class);
        return systemLog;
    }
}

三、技术使用中遇到的问题和解决过程

3.1 遇到的问题

问题描述:错误理解AOP中@After注解的作用。
  在进行团队项目时需要实现对多个接口的返回中增加一个相同的字段属性,一方面需要修改的接口是其他组员开发的,我并不想修改别人的代码,另一方面是有多个接口需要进行相同的操作后在返回值中增加一个相同的字段。出于这两个原因,我决定使用AOP来实现需求。
  期初我认为@After注解是在被增强的方法执行后执行,因此我在增强方法中对返回类A进行了修改,增加了所需字段使其成为返回类B并返回,但出现报错警告无法将返回类B转换为返回类A。

3.2 解决过程

  在查阅了AOP各注解的作用后发现了问题所在。我错误理解了@After的执行时间,@After的执行发生在增强的方法执行完成后Return之前,因此当我在@After中修改了返回类的类型时,被增强的方法在返回时会对返回值的类型进行校验或自动转换,于是便发生了类型转换错误冲突。
  最后通过修改原始方法中的返回类为返回类B,但在原始方法中对需要添加的字段并不做处理,使其为空,并在@After方法体中对其进行赋值,这样就保证了方法返回类的一致性,避免了类型转换的发生。
  虽说最后解决了问题,但感觉并不优雅,或许AOP并不适合用来修改方法返回类的类型。

四、总结

  AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,用于在应用程序中管理横切关注点。它通过将这些关注点从核心业务逻辑中分离出来,以模块化的方式实现重用和集中管理。
  在编码过程中我们应当发掘出可提取的重复的业务,使用AOP进行实现,这不仅可以减少重复的代码,同时提高了代码的可维护性,当该需求发生改变时仅需要修改一处的代码便可作用于全局,极大地减小了维护成本。

五、参考文献

1.SpringBoot整合AOP超详细教程 ——XiongHuyi
2.Spring Boot AOP - 面向切面编程——守望时空33
3.Spring——AOP原理及流程详解——Colins~

...全文
238 回复 打赏 收藏 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

688

社区成员

发帖
与我相关
我的任务
社区描述
2023年福州大学软件工程实践课程W班的教学社区
软件工程团队开发软件构建 高校 福建省·福州市
社区管理员
  • FZU_SE_teacherW
  • 张书旖
  • 郭渊伟
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧