Spring Boot之面向切面编程的设计

一、前言

  在使用Spring框架开发项目中接到一个需求—系统中要集成腾讯云IOT的接口,并且提供公共类或方法给其他各业务逻辑使用。根据需求确定在系统中封装一个IOT类库,包括各接口的请求封装(接口地址、传入参数、返回值、请求类型、错误编码、加密方式、请请求体、请求头),在查阅相关文档时腾讯IOT已经提供完善的SDK,所以直接使用Maven包管理器将SDK集成到系统,引入包、构建客户端对象、访问方法、返回结果。完成集成工作,提交代码后,开发人员在调试接口时遇到无法查看接口的请求日志的问题,所以对该集成进行调整,使用切面编程的思想,无侵入式的实现整个请求日志打印在控制台、文件或者日志存储中。对于这个调整使用面向切面编程是一个最佳实践,通过这篇文章来详细的学习Spring的面向切面编程。

  学习的路径是基本概念->使用场景->案例分析(当前案例)->实现原理由简单介绍,到如何在场景中选择使用,然后到具体使用中分析,最后熟悉实现原理内容,从而完整的熟悉切面编程。从使用切面编程技术点延伸到其他技术点这个方式一样适用,学的过程是从理论到实践,在从实践到原理,在从原理延伸其他内容。

二、定义

  AOP为Aspect Oriented Programming(面向切面编程),通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。既然要学习AOP,就必须从多个维度分析面向切面编程,如下所述。

  ①面向切面编程的关键点是切面(Aspect)和连接点(Joinpoint),其中切面是一组同时横切多个类的代码,封装成一个单元;连接点是程序执行的特定点,如方法的调用或执行。

  ②面向切面编程思想的实现方式,包括编译时增强,在编译期间通过修改字节码来实现AOP;类加载时增强,在类加载到JVM时通过字节码操作实现AOP;动态代理,在程序运行时,通过代理对象来实现AOP。当前讨论的例子使用的是动态代理方式。

  ③面向切面编程的通知(代码),定义切点出所要执行的动作,包括前置通知(Before),在方法执行前执行;后置通知(After),在方法执行后执行;返回通知(After Returning),在方法成功返回后执行;异常通知(After Throwing),在方法抛出异常后执行;环绕通知(Around),包围方法执行的前后。通过5个通知满足切面全部动作。

  ④面向切面编程的优势,减少重复代码,将横切关注点集中管理,避免在多个模块中重复相同的代码;提高模块化,将业务逻辑与非业务逻辑分离,提高代码的清晰度和可维护性;易于维护和更新,当需要修改横切关注点时,只需在一个地方进行更改。高内聚低耦合的设计。

二、编码

  在spring boot框架定义一个IUserService接口、UserServiceImpl实现类,UserServiceAspect的面向切面的类,通过控制器请求访问UserServiceImpl的方法,面向切面类会在方法执行前后拦截处理编写的业务逻辑,代码如下所示:

复制代码
package com.example.user.service;

import com.example.user.domain.User;

import java.util.List;

public interface IUserService {
    List<User> getUserName();

    int creatUser(User user);

    int updateUser(User user);

    int deleteUser(int userId);
}
复制代码
复制代码
package com.example.user.service.impl;

import com.example.user.domain.User;
import com.example.user.mapper.UserMapper;
import com.example.user.service.IUserService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;

@Service
public class UserServiceImpl implements IUserService {
    @Resource
    private UserMapper userMapper;

    @Override
    public List<User> getUserName() {
        return userMapper.selectList(User.builder().build());
    }

    @Override
    public int creatUser(User user) {
        return 0;
    }

    @Override
    public int updateUser(User user) {
        return 0;
    }

    @Override
    public int deleteUser(int userId) {
        return 0;
    }
}
复制代码
复制代码
package com.example.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

@Slf4j
@Aspect
@Component
public class UserServiceAspect {
    /**
     * 请求参数
     */
    @Before("execution(* com.example.user.service.impl.UserServiceImpl.*(..))")
    public void logMethodCall(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        List<Object> list = new ArrayList<>();
        for (Object arg : args) {
        }
        log.debug("请求接口={},参数={}", methodName, list);
    }

    /**
     * 响应参数
     */
    @AfterReturning(pointcut = "execution(* com.example.user.service.impl.UserServiceImpl.*(..))", returning = "result")
    public void logMethodReturn(JoinPoint joinPoint, Object result) {
        String methodName = joinPoint.getSignature().getName();
        log.debug("接口返回={},响应内容={}", methodName, result);
    }

    /**
     * 返回异常信息
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(pointcut =  "execution(* com.example.user.service.impl.UserServiceImpl.*(..))",throwing = "e")
    public void logMethodThrowing(JoinPoint joinPoint,Exception e) {
        String methodName = joinPoint.getSignature().getName();
        log.debug("接口异常={},异常内容={}", methodName, e.getMessage());
    }
}
复制代码
<dependency>
      <groupId>org.aspectj</groupId>
       <artifactId>aspectjweaver</artifactId>
       <version>1.9.7</version>
</dependency>

  通过在UserServiceAspect类中添加@Aspect定义一个切面,在方法中使用@Before、@AfterReturning、@AfterThrowing实现各个切入点的通知。

  在Spring框架中,AOP(面向切面编程)是一种编程范式,它允许开发者将横切关注点(例如日志记录、性能统计等)与应用程序的业务逻辑分离开来。Spring的AOP功能通过使用通知(Advice)来实现,通知是在特定的切点(JoinPoint)上执行的代码片段。Spring框架中的AOP通知类型包括以下几种:

  1. 前置通知(Before Advice):在目标方法执行之前执行的通知。前置通知可以用于验证输入参数、权限检查等操作。

  2. 后置通知(After Advice):在目标方法执行之后执行的通知。后置通知不会影响目标方法的返回值,即使在目标方法抛出异常后仍然会执行。

  3. 返回通知(After Returning Advice):在目标方法成功执行并返回结果后执行的通知。返回通知可以访问目标方法的返回值,并对其进行处理。

  4. 异常通知(After Throwing Advice):在目标方法抛出异常后执行的通知。异常通知可以对抛出的异常进行处理,或者选择不处理异常。

  5. 环绕通知(Around Advice):在目标方法执行之前和之后执行的通知。环绕通知可以完全控制目标方法的执行过程,可以决定是否继续执行目标方法。

三、总结

   面向切面编程是对面向对象的增强点,通过使用面向切面技术实现如权限、日志、事务等公共业务逻辑的提取,统一处理。在代码结构上减少重复代码、降低业务逻辑的耦合、低入侵的方式。

  

posted @   tuqunfu  阅读(76)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示