java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > SpringBoot整合Aop

SpringBoot整合Aop全过程

作者:小疙瘩的编程之路

AOP(面向切面编程)技术可以高效地解决日志记录、事务管理、权限控制等问题,日志记录通过自定义注解和切面类,自动记录方法调用详情,减少重复代码,事务管理方面,通过AOP可以在不改变业务代码的情况下,实现事务的自动开启、提交和回滚,保证数据一致性

AOP 可以做什么?

应用场景

日志记录

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
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}, #{costTime});")
    public void insert(OperateLog log);

}

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

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

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

@Retention(RetentionPolicy.RUNTIME)//运行时有效
@Target(ElementType.METHOD)//作用在方法上
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 operateLog = 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
}
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);
    }
}

心得

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

那为什么要结合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.Retention;
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的五种通知类型包括‌

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

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文