开发者

SpringBoot整合Aop全过程

目录
  • AOP 可以做什么?
  • 应用场景
  • 日志记录
    • 1.准备工作
    • 2.创建自定义注解和切面类
    • 3.实现日志记录(Around)
  • 管理事务
    • 1. 添加依赖
    • 2. 配置 MyBATis-Plus
    • 3. 创建事务切面
    • 4. 示例业务逻辑类
    • 5. 用户实体和 Mapper
    • 6. 创建控制器
    • 7. 启动类
    • 心得
    • 为什么做事务,做事务有啥用
    • 那为什么要结合aop去做事务
  • 权限控制
    • 1. 添加依赖
    • 2. 创建权限注解
    • 3. 创建切面类
    • 4. 使用权限注解
    • 5. 配置 Spring Boot
  • AOP的五种通知类型包括‌
    • 总结

      AOP 可以做什么?

      • 1. 日志记录
      • 自动记录方法的调用、参数、返回值和异常等信息,减少了在每个方法中添加日志代码的需要。
      • 2. 管理事务
      • 在方法执行前后自动处理事务的开启、提交和回滚,确保数据的一致性和完整性。
      • 3. 权限控制
      • 实现方法的访问控制,检查用户权限,确保只有授权用户才能执行特定操作。
      • 4. 性能监控
      • 自动收集方法的执行时间、调用次数等性能指标,帮助开发者进行性能分析和优化。
      • 5. 缓存管理
      • 实现方法结果的缓存,减少重复计算,提高系统性能。
      • 6. 异常处理
      • 统一android处理方法执行中的异常,简化异常捕获和处理逻辑。
      • 7. 输入验证
      • 在方法执行前对输入参数进行验证,保证数据的有效性和完整性。
      • 8. 动态代理
      • 创建代理对象,在不修改原有类的情况下添加额外的行为。

      应用场景

      • Web开发:用于请求处理、事务管理、安全控制等。
      • 企业应用:在服务层实现统一的日志和异常处理。
      • 微服务:在服务之间进行统一的监控和熔断处理。

      日志记录

      1.准备工作

      首先需要导入AOP依赖

      pom.XML

      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-aop</artifactId>
      </dependency>
      <dependency>
          <groupId>com.alibaba.fastjson2</groupId>
          <artifactId>fastjson2</artifactId>
          <version>2.0.50</version>
      </dependency>
      <dependency>
          <groupId>io.jsonwebtoken</groupId>
          <artifactId>jjwt</artifactId>
          <version>0.9.1</version>
      </dependency>

      然后需要创建日志记录数据库和实体类还有在Mapper层实现插入数据方法

      • 实体类
      //操作日志实体类
      @Data
      @NoArgsConstructor
      @AllArgsConstructor
      public class OperateLog {
          private Integer id; //主键ID
          private Integer operateUser; //操作人ID
          private LocalDateTime operateTime; //操作时间
          private String className; //操作类名
          private String methodName; //操作方法名
          private String methodParams; //操作方法参数
          private String returnValue; //操作方法返回值
          private Long costTime; //操作耗时
      }
      • Mapper接口
      @Mapper
      public interface OperateLogMapper {
      
          //插入日志数据
          @Insert("insert into operate_log (operate_user, operate_time, class_name, method_name, method_params, return_value, cost_time) " +
                  "values (#{operateUser}, #{operateTime}, #{className}, #{methodName}, #{methodParams}, #{returnValue}, #{coPmdYCWTstTime});")
          public void insert(OperateLog log);
      
      }

      2.创建自定义注解和切面类

      因为我们要记录的操作日志是对于数据的增,删,改所以采用@annotation来指定要为连接点的方法。

      而且在涉及到消耗时间的字段,所以需要采用@Around通知类型

      • 自定义注解类
      @Retention(RetentionPolicy.RUNTIME)//运行时有效
      @Target(ElementType.METH编程客栈OD)//作用在方法上
      public @interface Log {
      }

      3.实现日志记录(Around)

      首先需要需要记录的操作接口方法前加上@Log注解

      接下来在切面类中实现操作日志记录:

      @ASPect
      @Component
      public class LogAsper {
          @Autowired
          private OperateLogMapper operateLogMapper;
          @Autowired
          private HttpServletRequest request;
      
          @Around("@annotation(com.ly.springbootdemotlias.anno.Log)")//匹配加了Log注解的方法
          public Object recordLog(ProceedingJoinPoint joinPoint) throws Throwable {
              OperateLog ophttp://www.devze.comerateLog = new OperateLog();
              //获取操作人id,通过获取并解析jwt令牌
              String jwt = request.getHeader("token");
              Claims claims = JwtUtils.parseJwt((jwt));
              Integer operateUser =(Integer) claims.get("id");
              operateLog.setOperateUser(operateUser);
              //获取操作时间
              operateLog.setOperateTime(LocalDateTime.now());
              //获取操作类名
              operateLog.setClassName(joinPoint.getTarget().getClass().getName());
              //获取操作方法名
              operateLog.setMethodName(joinPoint.getSignature().getName());
              //获取操作方法参数
              operateLog.setMethodParams(Arrays.toString(joinPoint.getArgs()));
              //获取开始时间
              long begin = System.currentTimeMillis();
              //获取操作方法返回值
              Object result = joinPoint.proceed();
              String returnValue = JSONObject.toJSONString(result);
              operateLog.setReturnValue(returnValue.toString());
              //获取操作结束时间
              long end = System.currentTimeMillis();
              //获取操作耗时
              operateLog.setCostTime((end-begin));
              operateLogMapper.insert(operateLog);
              return  result;
      
          }
      }

      管理事务

      1. 添加依赖

      在 pom.xml 中添加 MyBatis-Plus 和 AOP 的依赖

      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-aop</artifactId>
      </dependency>
      <dependency>
          <groupId>com.baomidou</groupId>
          <artifactId>mybatis-plus-boot-starter</artifactId>
          <version>3.5.0</version> <!-- 根据需要选择版本 -->
      </dependency>
      <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-Java</artifactId>
          <scope>runtime</scope>
      </dependency>

      2. 配置 MyBatis-Plus

      在 application.yml 中配置 MyBatis-Plus 和数据源:

      spring:
        datasource:
          url: jdbc:mysql://localhost:3306/your_database
          username: your_username
          password: your_password
          driver-class-name: com.mysql.cj.jdbc.Driver
        mybatis-plus:
          mapper-locations: classpath*:/mappers/**/*.xml

      3. 创建事务切面

      创建一个事务切面类:

      import org.aspectj.lang.ProceedingJoinPoint;
      import org.aspectj.lang.annotation.Around;
      import org.aspectj.lang.annotation.Aspect;
      import org.springframework.stereotype.Component;
      import org.springframework.transaction.annotation.Transactional;
      
      @Aspect
      @Component
      public class TransactionAspect {
      
          @Transactional
          @Around("execution(* com.yourpackage.service..*(..))") // 指定需要事务的业务逻辑包
          public Object manageTransaction(ProceedingJoinPoint joinPoint) throws Throwable {
              Object result;
              try {
                  result = joinPoint.proceed(); // 执行目标方法
                  return result;
              } catch (Exception e) {
                  throw e; // 抛出异常以触发回滚
              }
          }
      }

      4. 示例业务逻辑类

      创建一个服务类,使用 MyBatis-Plus 进行数据库操作:

      import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
      import org.springframework.stereotype.Service;
      
      @Service
      public class UserService extends ServiceImpl<UserMapper, User> {
      
          public void createUser(User user) {
              this.save(user); // 调用 MyBatis-Plus 的 save 方法
          }
      }

      5. 用户实体和 Mapper

      • 定义用户实体:
      import com.baomidou.mybatisplus.annotation.TableId;
      import com.baomidou.mybatisplus.annotation.TableName;
      
      @TableName("users") // 数据库表名
      public class User {
          @TableId
          private Long id;
          private String name;
      
          // getters and setters
      }
      • Mapper 接口:
      import com.baomidou.mybatisplus.core.mapper.BaseMapper;
      
      public interface UserMapper extends BaseMapper<User> {
          // 可以自定义额外的方法
      }

      6. 创建控制器

      在控制器中调用 UserService 的 createUser 方法:

      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.web.bind.annotation.*;
      
      @RestController
      @RequestMapping("/users")
      public class UserController {
      
          @Autowired
          private UserService userService;
      
          @PostMapping
          public String createUser(@RequestBody User user) {
              userService.createUser(user); // 调用 UserService 的 createUser 方法
              return "User created successfully"; // 返回成功消息
          }
      }

      7. 启动类

      import org.springframework.boot.SpringApplication;
      import org.springframework.boot.autoconfigure.SpringBootApplication;
      
      @SpringBootApplication
      public class Application {
          public static void main(String[] args) {
              SpringApplication.run(Application.class, args);
          }
      }

      心得

      • 通过以上步骤,你可以在 Spring Boot 项目中整合 AOP 和 MyBatis-Plus,实现简单而有效的事务管理。
      • 这样,无论是在正常情况下提交事务,还是在出现异常时回滚事务,都能得到很好的支持。

      为什么做事务,做事务有啥用

      • 事务确保一组操作要么全部成功,要么全部失败,从而保持数据的一致性和完整性。
      • 它们在处理银行转账、订单处理等场景时尤其重要,可以防止数据不一致、部分更新或系统故障导致的数据丢失。
      • 通过事务,确保即使在异常情况下,数据状态依然可靠。

      那为什么要结合aop去做事务

      • 结合 AOP(面向切面编程)可以将事务管理的逻辑与业务逻辑分离,提高代码的可维护性和可读性。
      • 通过 AOP,可以在不修改业务代码的情况下,集中管理事务,简化代码结构,并实现事务的自动化管理,减少重复代码,提高开发效率。

      权限控制

      1. 添加依赖

      在 pom.xml 中添加 AOP 相关依赖:

      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-aop</artifactId>
      </dependency>

      2. 创建权限注解

      定义一个自定义注解 @RequiresPermission:

      import java.lang.annotation.ElementType;
      import java.lang.annotation.Retentionjavascript;
      import java.lang.annotation.RetentionPolicy;
      import java.lang.annotation.Target;
      
      @Target(ElementType.METHOD)
      @Retention(RetentionPolicy.RUNTIME)
      public @interface RequiresPermission {
          String value();
      }

      3. 创建切面类

      创建切面类,使用 @Aspect 注解来定义切面,处理权限控制逻辑:

      import org.aspectj.lang.ProceedingJoinPoint;
      import org.aspectj.lang.annotation.Around;
      import org.aspectj.lang.annotation.Aspect;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Component;
      
      @Component
      @Aspect
      public class PermissionAspect {
      
          @Autowired
          private UserService userService; // 获取用户服务
      
          @Around("@annotation(requiresPermission)") // 拦截使用 @RequiresPermission 注解的方法
          public Object checkPermission(ProceedingJoinPoint joinPoint, RequiresPermission requiresPermission) throws Throwable {
              String requiredPermission = requiresPermission.value(); // 获取所需权限
      
              // 权限检查逻辑
              if (!hasPermission(requiredPermission)) {
                  throw new SecurityException("Permission denied: " + requiredPermission);
              }
      
              return joinPoint.proceed(); // 执行目标方法
          }
      
          private boolean hasPermission(String permission) {
              User currentUser = userService.getCurrentUser(); // 获取当前用户
      
              if (currentUser == null) {
                  return false; // 用户未登录
              }
      
              return currentUser.getPermissions().contains(permission); // 检查权限
          }
      }

      在使用 AOP 时,@RequiresPermission 注解是一个标记,它本身并不直接传递参数。Spring AOP 在拦截带有该注解的方法时,会自动创建该注解的实例,并将其作为参数传递给

      checkPermission 方法。这是通过 AOP 框架的代理机制实现的,而不是通过显式调用。具体来说,当被注解的方法被调用时,AOP 会拦截这个调用并注入相应的注解信息。

      permission 参数来源于 @RequiresPermission 注解的 value() 方法。在 checkPermission 方法中,当该方法拦截到一个使用 @RequiresPermission 注解的方法时,Spring AOP 会将该注解的实例作为第二个参数传递给 checkPermission 方法。因此,你可以通过调用 requiresPermission.value() 来获取注解中定义的权限字符串。

      4. 使用权限注解

      在需要进行权限控制的方法上使用自定义注解

      import org.springframework.web.bind.annotation.GetMapping;
      import org.springframework.web.bind.annotation.RestController;
      
      @RestController
      public class MyController {
      
          @GetMapping("/admin")
          @RequiresPermission("ADMIN_Access")
          public String adminAccess() {
              return "Welcome, admin!";
          }
      }

      5. 配置 Spring Boot

      确保 Spring Boot 应用程序能够扫描到切面和自定义注解。通常情况下,只需在主类中添加 @EnableAspectJAutoProxy 注解:

      import org.springframework.boot.SpringApplication;
      import org.springframework.boot.autoconfigure.SpringBootApplication;
      import org.springframework.context.annotation.EnableAspectJAutoProxy;
      
      @SpringBootApplication
      @EnableAspectJAutoProxy
      public class MyApplication {
          public static void main(String[] args) {
              SpringApplication.run(MyApplication.class, args);
          }
      }

      @EnableAspectJAutoProxy 注解的作用是启用 Spring 的 AOP(面向切面编程)支持,允许使用 AspectJ 的代理机制来实现切面。具体来说,它有以下几个功能:

      1.启用代理

      它告诉 Spring 创建代理对象,这些代理对象可以拦截方法调用并执行切面逻辑。

      2.支持注解

      当你在代码中使用注解(如 @Around、@Before 等)时,@EnableAspectJAutoProxy 会使这些注解生效,从而实现方法拦截和切面逻辑的执行。

      3.简化配置

      通过添加这个注解,开发者不需要额外的 XML 配置,只需通过注解来定义切面和增强,减少了配置的复杂性。

      在主类中添加这个注解后,Spring Boot 应用会自动扫描到切面类并将其注册到应用上下文中,从而实现所需的 AOP 功能。

      AOP的五种通知类型包括‌

      • 前置通知(@Before)‌
      • 后置通知(@After)‌
      • 返回通知(@AfterReturning)‌
      • 异常通知(@AfterThrowing)‌
      • 环绕通知(@Around)‌

      每种通知类型都有其特定的用途和场景,可以根据实际需求选择使用。

      总结

      以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程客栈(www.devze.com)。

      0

      上一篇:

      下一篇:

      精彩评论

      暂无评论...
      验证码 换一张
      取 消

      最新开发

      开发排行榜