Spring AOP 详细深入讲解+代码示例

news2024/11/15 20:40:02

Spring AOP

这里是引用

一,介绍

spring aop工作原理图

在这里插入图片描述

1.什么是spring aop

Spring AOP(Aspect-Oriented Programming)是Spring框架提供的一种面向切面编程的技术。它通过将横切关注点(例如日志记录、事务管理、安全性检查等)从主业务逻辑代码中分离出来,以模块化的方式实现对这些关注点的管理和重用。

在Spring AOP中,切面(Aspect)是一个模块化的关注点,它可以跨越多个对象,例如日志记录、事务管理等。切面通过定义切点(Pointcut)和增强(Advice)来介入目标对象的方法执行过程。

切点是一个表达式,用于匹配目标对象的一组方法,在这些方法执行时切面会被触发。增强则定义了切面在目标对象方法执行前、执行后或抛出异常时所要执行的逻辑。

Spring AOP提供了以下几种类型的增强:

  1. 前置增强(Before Advice):在目标方法执行之前执行的逻辑。
  2. 后置增强(After Advice):在目标方法执行之后执行的逻辑,不管目标方法是否抛出异常。
  3. 返回增强(After Returning Advice):在目标方法正常返回时执行的逻辑。
  4. 异常增强(After Throwing Advice):在目标方法抛出异常时执行的逻辑。
  5. 环绕增强(Around Advice):在目标方法执行前后都可以执行的逻辑,它可以完全控制目标方法的执行。

Spring AOP通过使用动态代理技术,在目标对象方法执行时将切面的逻辑织入到目标对象的方法中。这样,我们可以在不修改原始业务代码的情况下,实现横切关注点的统一处理。

总而言之,Spring AOP是一种通过切面将横切关注点模块化的技术,它提供了一种简洁的方式来管理和重用跨越多个对象的关注点逻辑。

2.为什么要用spring aop

使用Spring AOP的主要原因是它可以帮助我们更好地管理各种横切关注点,例如日志记录、事务管理、安全性检查等。以下是一些使用Spring AOP的优点:

  1. 模块化:Spring AOP将横切关注点从主业务逻辑代码中分离出来,以模块化的方式实现对这些关注点的管理和重用。这样,我们可以更容易地维护代码,并且可以将同一个关注点的逻辑应用到多个方法或类中。

  2. 非侵入式:使用Spring AOP时,我们不需要修改原始业务逻辑代码,只需要在切点和增强中定义我们所需要的逻辑即可。这样,我们可以保持原始代码的简洁性和可读性。

  3. 可重用性:我们可以将同一个切面应用于多个目标对象进行横切处理。这样,我们可以提高代码的重用性,并且可以更加方便地维护和更新切面逻辑。

  4. 松耦合:AOP可以减少各个业务模块之间的耦合度,这是因为我们可以将某些通用的逻辑作为切面来实现,而不是直接在各个业务模块中实现。这样可以使得各个业务模块之间更加独立,从而提高代码的可维护性。

总之,使用Spring AOP可以帮助我们更好地管理和重用横切关注点逻辑,使得代码更易于维护和理解,并且可以提高代码的可重用性和灵活性。

3.spring aop的好处

Spring AOP(Aspect-Oriented Programming)是一种编程范式,它通过切面(Aspect)的概念来提供对横切关注点的支持。在传统的面向对象编程中,我们将功能逻辑以对象的形式组织起来,而在面对特定需求时,常常需要在多个对象或方法中添加相同的功能代码,例如日志记录、事务管理等。这样的代码重复不仅增加了代码量,也使得代码难以维护和理解。

Spring AOP的目标是通过将横切关注点与主业务逻辑进行解耦,实现关注点的模块化和可重用性。横切关注点指的是与业务逻辑无关但又必须在多个地方进行处理的功能,如日志记录、事务管理、异常处理等。

在Spring AOP中,我们可以定义切面(Aspect),切面由切点(Pointcut)、通知(Advice)和连接点(Joinpoint)组成。切点定义了哪些连接点会被切面所影响,通知定义了在切点处执行的逻辑,而连接点则表示程序执行过程中的某个特定点。

Spring AOP的工作原理是通过动态代理的方式,在运行时将切面逻辑织入到目标对象的方法中,从而实现对横切关注点的处理。

使用Spring AOP的好处包括:

  1. 模块化:将横切关注点从业务逻辑中分离出来,以模块化的方式进行管理。
  2. 非侵入性:不需要修改原始业务逻辑代码,只需在切面中定义关注点的逻辑。
  3. 可重用性:将同一切面应用于多个目标对象,提高代码的可重用性。
  4. 松耦合:减少各业务模块之间的耦合度,提高代码的可维护性。
  5. 提高系统性能和可扩展性:通过减少重复代码和逻辑,提高系统性能和可扩展性。

总结而言,Spring AOP通过切面的概念,实现了对横切关注点的解耦和重用,提高了代码的组织性、可维护性和可扩展性。

4.核心概念

是的,Spring AOP 的核心概念包括切面、连接点、通知、切点以及织入。下面我将对这些概念做一些简要的解释:

  1. 切面(Aspect):切面是一个模块化的横切关注点实现,它包括了连接点和通知。可以通过配置文件、注解等方式定义切面。

  2. 连接点(Joinpoint):程序中能够被切面插入的点,典型的连接点包括方法调用、方法执行过程中的某个时点等等。

  3. 通知(Advice):在连接点处执行的代码。通知分为各种类型,如前置通知、后置通知、环绕通知等。

  4. 切点(Pointcut):用于定义哪些连接点上应该应用通知。切点通过表达式进行定义,如匹配所有 public 方法或匹配某个包下的所有方法等。

  5. 织入(Weaving):指将切面应用到目标对象并创建新的代理对象的过程。织入可以在运行时完成,也可以在编译时完成。 Spring AOP 提供了两种织入方式:编译期织入和运行期织入。

除此之外,Spring AOP 还有其他常用的概念,如目标对象(Target)、代理对象(Proxy)等。目标对象是含有连接点的对象,而代理对象是 Spring AOP 创建的一个包含切面代码的对象。

以上就是 Spring AOP 的核心概念,它们共同构成了切面编程的基础。

5.spring aop JDK动态代理和cglib动态代理的区别

Spring AOP 支持两种类型的动态代理:JDK 动态代理和 CGLIB 动态代理。它们之间有以下区别:

  1. 基于类型:JDK 动态代理是基于接口的代理,而 CGLIB 动态代理是基于类的代理。

  2. 代理对象创建:JDK 动态代理通过 Java 自带的 java.lang.reflect.Proxy 创建代理对象,该对象必须实现一个或多个接口。CGLIB 动态代理通过字节码生成技术创建代理对象,无需目标类实现接口,直接继承目标类。

  3. 性能:JDK 动态代理在运行时需要使用反射,导致较低的性能。CGLIB 动态代理通过生成字节码,避免了反射,因此通常比 JDK 动态代理速度更快。

  4. 对象类型:JDK 动态代理只能代理具有接口的目标对象,不适用于没有接口的类。CGLIB 动态代理可以代理任何类,包括没有实现接口的类。

  5. 继承:JDK 动态代理只能代理目标对象的接口方法,不能代理其父类中的方法。CGLIB 动态代理可以代理目标类及其父类中的方法。

综上所述,选择使用 JDK 动态代理还是 CGLIB 动态代理取决于具体的需求和场景。如果目标对象实现了接口并且对性能要求较高,可以选择 JDK 动态代理;如果目标对象没有实现接口或者对性能要求不那么苛刻,可以选择 CGLIB 动态代理。默认情况下,Spring AOP 使用 JDK 动态代理,但在某些情况下会自动切换到 CGLIB 动态代理。

6.aop和oop的区别

AOP (Aspect-Oriented Programming) 和 OOP (Object-Oriented Programming) 都是面向对象编程的范式,但它们关注的角度不同。下面是它们之间的区别:

  1. 技术视角:OOP 是一种代码组织技术,它通过将数据和操作封装在对象中来实现模块化和复用。AOP 是一种编程范式,它允许开发者在执行过程中(而非编译期)动态地添加、删除或修改代码的功能。

  2. 关注点:OOP 关注对象的内部结构和行为,其目标是更好地描述和设计一个系统的真实世界概念。AOP 关注横切关注点(Cross-cutting Concerns),即存在于应用程序各个层面的相同问题,如日志、事务、安全等。

  3. 实现方式:OOP 是通过类和接口来实现封装、继承和多态等特性,使得类能够更好地表达问题领域内的概念。AOP 是通过将通用功能抽取为切面(Aspect),并与核心业务逻辑混合使用,来实现这些横切关注点。

  4. 关注点分离:OOP 面对复杂系统可能导致代码重复或紧密耦合的问题,而 AOP 采用横切关注点的方式来解决这些问题,使得系统功能更加组合和重用。

综上所述,OOP 是一种面向对象的编程技术,它关注如何将数据和操作组织在一起。而 AOP 通过提取和组合通用功能以及横跨多个层次的共性代码来实现程序逻辑的复用和可维护性的增强。

二,基本使用代码示例

1.spring 版

​下面是一个简单的 Spring AOP 的代码示例:

  1. 创建目标类(Target Class):
public class UserService {
    public void addUser(String username) {
        System.out.println("Adding user: " + username);
    }
}
  1. 定义切面类(Aspect Class):
@Aspect
public class LoggingAspect {

    @Before("execution(* com.example.UserService.addUser(String)) && args(username)")
    public void beforeAddUser(String username) {
        System.out.println("Before adding user: " + username);
    }

    @AfterReturning("execution(* com.example.UserService.addUser(String)) && args(username)")
    public void afterAddUser(String username) {
        System.out.println("After adding user: " + username);
    }
}
  1. 配置 Spring AOP:
<bean id="userService" class="com.example.UserService"/>
<bean id="loggingAspect" class="com.example.LoggingAspect"/>

<aop:config>
    <aop:aspect ref="loggingAspect">
        <aop:before method="beforeAddUser" pointcut="execution(* com.example.UserService.addUser(String))"/>
        <aop:after-returning method="afterAddUser" pointcut="execution(* com.example.UserService.addUser(String))"/>
    </aop:aspect>
</aop:config>

这个示例代码中,UserService 类是我们的目标类,LoggingAspect 类是切面类。在切面类中,@Before 注解用于在 addUser 方法执行前打印日志,@AfterReturning 注解用于在 addUser 方法成功执行后打印日志。

配置文件中定义了目标类和切面类的实例,并使用 aop:config 标签来进行配置。通过 aop:before 和 aop:after-returning 标签分别将 beforeAddUser 和 afterAddUser 方法与切点关联起来。

当调用 UserService 的 addUser 方法时,Spring AOP 会根据配置自动触发切面逻辑,从而实现日志打印的功能。

这只是一个简单的示例,实际使用中可以根据需求定义更复杂的切面和通知逻辑。

2.Spring Boot版

下面是一个基于 Spring Boot 的 AOP 示例代码:

  1. 创建目标类(Target Class):
@Service
public class UserService {
    public void addUser(String username) {
        System.out.println("Adding user: " + username);
    }
}
  1. 定义切面类(Aspect Class):
@Aspect
@Component
public class LoggingAspect {

    @Before("execution(* com.example.UserService.addUser(String)) && args(username)")
    public void beforeAddUser(String username) {
        System.out.println("Before adding user: " + username);
    }

    @AfterReturning("execution(* com.example.UserService.addUser(String)) && args(username)")
    public void afterAddUser(String username) {
        System.out.println("After adding user: " + username);
    }
}
  1. 启用 Spring Boot AOP:

在启动类上添加 @EnableAspectJAutoProxy 注解,开启 Spring Boot 的 AOP 功能。

@SpringBootApplication
@EnableAspectJAutoProxy
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

这个示例代码中,UserService 类是我们的目标类,LoggingAspect 类是切面类。在切面类中,@Before 注解用于在 addUser 方法执行前打印日志,@AfterReturning 注解用于在 addUser 方法成功执行后打印日志。

启动类上添加了 @EnableAspectJAutoProxy 注解,开启了 Spring Boot 的 AOP 功能。

当调用 UserService 的 addUser 方法时,Spring AOP 会根据配置自动触发切面逻辑,从而实现日志打印的功能。

这只是一个简单的示例,实际使用中可以根据需求定义更复杂的切面和通知逻辑。

3.Spring Cloud版

以下是使用 Spring Cloud 中 AOP 的一个简单示例:

假设我们有一个名为 HelloService 的服务,该服务需要记录日志。我们可以通过 AOP 来实现此功能。

  1. 创建 LogAspect 切面类,用于记录日志:
@Aspect
@Component
public class LogAspect {

    private static Logger logger = LoggerFactory.getLogger(LogAspect.class);

    @Pointcut("execution(public * com.example.service.HelloService.*(..))")
    public void log() {}

    @Before("log()")
    public void doBefore(JoinPoint joinPoint) {
        logger.info("method {} start", joinPoint.getSignature().getName());
    }

    @AfterReturning(returning = "result", pointcut = "log()")
    public void doAfterReturning(Object result) {
        logger.info("method return value: {}", result);
        logger.info("method end");
    }

    @AfterThrowing(throwing = "ex", pointcut = "log()")
    public void doAfterThrowing(Throwable ex) {
        logger.error("method throw exception: {}", ex.getMessage());
    }
}
  1. HelloService 中添加业务方法:
@Service
public class HelloService {

    public String sayHello(String name) {
        return "Hello, " + name;
    }

}
  1. 在应用程序启动类中声明 AOP 配置:
@SpringBootApplication
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public LogAspect logAspect() {
        return new LogAspect();
    }
}
  1. 启动应用程序,并调用 HelloServicesayHello 方法,观察控制台日志输出:
@RestController
public class HelloController {

    @Autowired
    private HelloService helloService;

    @GetMapping("/hello")
    public String sayHello(@RequestParam String name) {
        return helloService.sayHello(name);
    }
}

通过以上步骤,我们就成功地使用 Spring Cloud AOP 实现了一个简单的日志记录功能。在业务逻辑中引入切面可以很方便地实现各类横切关注点。

三,Spring AOP 应用场景加代码示例

接下来用spring boot 方式去实现代码

1.事物管理

在 Spring Boot 中使用 AOP 实现事务管理的示例:

  1. 添加 Spring Boot Starter JDBC 和 Spring Boot Starter AOP 依赖。
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
  1. application.properties 文件中配置数据源信息。
spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
  1. 创建一个 Service 类 UserService,实现添加用户和查询用户列表的方法,并添加事务注解 @Transactional
@Service
public class UserService {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Transactional
    public void addUser(String name, Integer age) {
        jdbcTemplate.update("insert into user(name,age) values (?,?)", name, age);
    }

    public List<User> findUsers() {
        return jdbcTemplate.query("select * from user", new BeanPropertyRowMapper<>(User.class));
    }
}
  1. 创建一个 Aspect 类 TransactionAspect,实现事务管理的切面逻辑,并添加事务注解 @Transactional
@Aspect
@Component
public class TransactionAspect {

    @Autowired
    private PlatformTransactionManager transactionManager;

    @Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)")
    public void tx() {}

    @Around("tx()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
        Object result;
        try {
            result = point.proceed();
            transactionManager.commit(status);
        } catch (Throwable e) {
            transactionManager.rollback(status);
            throw e;
        }
        return result;
    }

}
  1. 在应用程序启动类中声明 AOP 配置。
@SpringBootApplication
@EnableTransactionManagement
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public TransactionAspect transactionAspect() {
        return new TransactionAspect();
    }

    @Bean
    public DataSource dataSource() {
        return new DruidDataSource();
    }

    @Bean
    public JdbcTemplate jdbcTemplate() {
        return new JdbcTemplate(dataSource());
    }

    @Bean
    public PlatformTransactionManager txManager() {
        return new DataSourceTransactionManager(dataSource());
    }
}

通过以上步骤,我们就成功地使用 Spring Boot AOP 实现了一个简单的事务管理。在 Service 层添加 @Transactional 注解,即可自动开启事务,无需手动操作数据库连接和事务绑定。

2.日志记录

在 Spring Boot 中使用 AOP 实现日志记录管理的示例:

  1. 添加 Spring Boot Starter AOP 依赖。
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
  1. 创建一个 Aspect 类 LogAspect,实现日志记录的切面逻辑。在方法执行前输出日志记录开始的信息,在方法执行后输出日志记录结束的信息及方法的返回值。
@Aspect
@Component
public class LogAspect {

    private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);

    @Pointcut("execution(* com.example.service..*.*(..))")
    public void log() {}

    @Before("log()")
    public void before(JoinPoint point) {
        logger.info("start execute method: {}.{}", point.getTarget().getClass().getName(), point.getSignature().getName());
    }

    @AfterReturning(pointcut = "log()", returning = "returnValue")
    public void afterReturning(JoinPoint point, Object returnValue) {
        logger.info("end execute method: {}.{}. return value is {}", point.getTarget().getClass().getName(), point.getSignature().getName(), returnValue);
    }
}
  1. 配置日志输出格式和级别。我们在 application.properties 文件中配置以下属性:
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
logging.level.com.example=DEBUG

这里指定了控制台日志输出格式为带时间戳、线程号、日志级别、类名以及消息。并设置了 com.example 包下的日志级别为 DEBUG。

  1. 在应用程序启动类中声明 AOP 配置。
@SpringBootApplication
@EnableAspectJAutoProxy
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public LogAspect logAspect() {
        return new LogAspect();
    }

}

通过以上步骤,我们就成功地使用 Spring Boot AOP 实现了一个简单的日志记录管理。在业务逻辑中引入切面可以很方便地实现各类横切关注点。

3.安全控制

在 Spring Boot 中使用 AOP 实现安全控制管理的示例:

  1. 添加 Spring Boot Starter AOP 和 Spring Security 依赖。
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
  1. 创建一个 Aspect 类 SecurityAspect,实现安全控制的切面逻辑。在方法执行前检查用户是否具有相应的权限,如果没有权限,则抛出异常。
@Aspect
@Component
public class SecurityAspect {

    @Autowired
    private HttpServletRequest request;

    @Pointcut("execution(* com.example.service..*.*(..))")
    public void checkPermission() {}

    @Before("checkPermission()")
    public void before(JoinPoint point) {
        // 获取当前登录用户的权限
        Set<String> permissions = getCurrentUserPermissions();
        
        // 获取方法上的注解
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        RequiredPermission permissionAnnotation = method.getAnnotation(RequiredPermission.class);
        
        // 检查用户是否具有相应权限
        if (permissionAnnotation != null && !permissions.contains(permissionAnnotation.value())) {
            throw new AccessDeniedException("Access Denied");
        }
    }

    private Set<String> getCurrentUserPermissions() {
        // 从请求中获取当前登录用户的权限,可以根据实际情况进行实现
        // ...
        return Collections.emptySet();
    }
}
  1. 创建一个自定义注解 RequiredPermission,用于标注需要进行权限检查的方法。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiredPermission {
    String value();
}
  1. 在应用程序启动类中声明 AOP 配置。
@SpringBootApplication
@EnableAspectJAutoProxy
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public SecurityAspect securityAspect() {
        return new SecurityAspect();
    }

}
  1. 在 Service 层的方法上使用 @RequiredPermission 注解标注需要进行权限检查的方法,并传入相应的权限值。
@Service
public class UserService {

    @RequiredPermission("user:add")
    public void addUser(String name, Integer age) {
        // 添加用户的逻辑
    }

    @RequiredPermission("user:list")
    public List<User> findUsers() {
        // 查询用户列表的逻辑
        return Collections.emptyList();
    }
}

通过以上步骤,我们就成功地使用 Spring Boot AOP 和 Spring Security 实现了一个简单的安全控制管理。在 Service 层的方法上使用 @RequiredPermission 注解标注需要进行权限检查的方法,在切面中进行权限验证。如果当前登录用户没有相应的权限,则抛出访问拒绝异常。

4.参数校验

在 Spring Boot 中使用 AOP 实现参数校验管理的示例:

  1. 添加 Spring Boot Starter AOP 和 Hibernate Validator 依赖。
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>
  1. 创建一个 Aspect 类 ValidationAspect,实现参数校验的切面逻辑。在方法执行前进行参数校验,如果校验失败,则抛出异常。
@Aspect
@Component
public class ValidationAspect {

    @Pointcut("execution(* com.example.service..*.*(..))")
    public void validate() {}

    @Before("validate()")
    public void before(JoinPoint point) {
        Object[] args = point.getArgs();
        for (Object arg : args) {
            validateObject(arg);
        }
    }

    private void validateObject(Object obj) {
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        Validator validator = factory.getValidator();
        Set<ConstraintViolation<Object>> violations = validator.validate(obj);
        if (!violations.isEmpty()) {
            throw new ConstraintViolationException(violations);
        }
    }
}
  1. 在 Service 层的方法参数上使用标准的 Java Bean Validation 注解进行参数校验。例如,使用 @NotNull@Min@Max 等注解。
@Service
public class UserService {

    public void addUser(@NotNull String name, @Min(18) @Max(99) Integer age) {
        // 添加用户的逻辑
    }

}
  1. 在应用程序启动类中声明 AOP 配置。
@SpringBootApplication
@EnableAspectJAutoProxy
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public ValidationAspect validationAspect() {
        return new ValidationAspect();
    }

}

通过以上步骤,我们就成功地使用 Spring Boot AOP 和 Hibernate Validator 实现了一个简单的参数校验管理。在 Service 层的方法参数上使用标准的 Java Bean Validation 注解进行参数校验,在切面中进行参数校验。如果校验失败,则抛出约束违规异常 ConstraintViolationException

5.性能监控

在 Spring Boot 中使用 AOP 实现性能监控的示例:

  1. 添加 Spring Boot Starter AOP 依赖。
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
  1. 创建一个 Aspect 类 PerformanceAspect,实现性能监控的切面逻辑。在方法执行前记录开始时间,在方法执行后计算执行时间,并输出到日志中。
@Aspect
@Component
public class PerformanceAspect {

    private static final Logger LOGGER = LoggerFactory.getLogger(PerformanceAspect.class);

    @Around("execution(* com.example.service..*.*(..))")
    public Object measurePerformance(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().toShortString();
        long startTime = System.currentTimeMillis();
        LOGGER.info("Start executing method: {}", methodName);

        Object result = joinPoint.proceed();

        long endTime = System.currentTimeMillis();
        long executionTime = endTime - startTime;
        LOGGER.info("Finish executing method: {}. Execution time: {} ms", methodName, executionTime);
        return result;
    }
}
  1. 在应用程序启动类中声明 AOP 配置。
@SpringBootApplication
@EnableAspectJAutoProxy
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public PerformanceAspect performanceAspect() {
        return new PerformanceAspect();
    }

}

通过以上步骤,我们就成功地使用 Spring Boot AOP 实现了一个简单的性能监控。在切面中使用 @Around 注解拦截所有 Service 层的方法,并记录方法的开始时间和结束时间,计算执行时间,并输出到日志中。这样我们就可以在日志中查看每个方法的执行时间,以进行性能监控和优化。

6.数据缓存

在 Spring Boot 中使用 AOP 实现数据缓存的示例:

  1. 添加 Spring Boot Starter AOP 和 Spring Boot Starter Data Redis 依赖。
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  1. 配置 Redis 的连接信息,在 application.propertiesapplication.yml 文件中添加以下配置:
spring.redis.host=127.0.0.1
spring.redis.port=6379
  1. 创建一个 Aspect 类 CachingAspect,实现数据缓存的切面逻辑。在方法执行前尝试从缓存中获取数据,如果找到了,则直接返回缓存数据;如果没有找到,则执行方法逻辑并将结果存入缓存。
@Aspect
@Component
public class CachingAspect {

    private static final Logger LOGGER = LoggerFactory.getLogger(CachingAspect.class);

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Around("execution(* com.example.service..*.*(..))")
    public Object cacheData(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().toShortString();
        String cacheKey = methodName + Arrays.toString(joinPoint.getArgs());

        // 尝试从缓存中获取数据
        ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
        Object cachedData = valueOperations.get(cacheKey);

        if (cachedData != null) {
            LOGGER.info("Retrieve data from cache for method: {}", methodName);
            return cachedData;
        }

        // 从数据库等数据源获取数据
        Object fetchedData = joinPoint.proceed();

        // 将数据存入缓存
        valueOperations.set(cacheKey, fetchedData);
        LOGGER.info("Cache data for method: {}", methodName);

        return fetchedData;
    }
}
  1. 在应用程序启动类中配置 RedisTemplate。
@SpringBootApplication
@EnableAspectJAutoProxy
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        return redisTemplate;
    }

    @Bean
    public CachingAspect cachingAspect() {
        return new CachingAspect();
    }

}

通过以上步骤,我们就成功地使用 Spring Boot AOP 和 Redis 实现了一个简单的数据缓存。在切面中使用 @Around 注解拦截所有 Service 层的方法,将方法名和参数作为缓存的键,尝试从 Redis 缓存中获取数据。如果找到了缓存数据,则直接返回;如果没有找到,则执行方法逻辑并将结果存入缓存。这样可以提高数据读取的性能,减少对数据源的访问。需要注意的是,缓存的键需要保证唯一性,不同的方法和参数应该使用不同的缓存键。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1121901.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

solidworks 2024新功能之-让您的工作更加高效

您可以创建杰出的设计&#xff0c;并将这些杰出的设计将融入产品体验中。为了帮您简化和加快由概念到成品的产品开发流程&#xff0c;SOLIDWORKS 2024 涵盖全新的用户驱动型增强功能&#xff0c;致力于帮您实现更智能、更快速地与您的团队和外部合作伙伴协同工作。 SOLIDWORKS…

【JavaEE初阶】 线程池详解与实现

文章目录 &#x1f334;线程池的概念&#x1f384;标准库中的线程池&#x1f340;ThreadPoolExecutor 类&#x1f6a9;corePoolSize与maximumPoolSize&#x1f6a9;keepAliveTime&#x1f6a9;ThreadFactory&#x1f6a9;workQueue&#x1f6a9;RejectedExecutionHandler handl…

思科披露新的IOS XE零日漏洞,用于部署恶意软件植入

导语&#xff1a;思科最近披露了一个新的高危零日漏洞&#xff08;CVE-2023-20273&#xff09;&#xff0c;该漏洞被积极利用来在已经通过本周早些时候披露的CVE-2023-20198零日漏洞遭到侵害的IOS XE设备上部署恶意植入物。 漏洞披露 思科最近披露了一款名为CVE-2023-20273的高…

Leetcode-Easy题解1-回文数字

目录 解法1解法2 解法1 自己的想法,直接转成字符串首尾俩下标同时遍历比较 class Solution {public boolean isPalindrome(int x) {if(x<0){return false;}String strString.valueOf(x);int i0;for (;i<str.length()>>1;i){if(str.charAt(i)!str.charAt(str.leng…

Unity中Shader阴影的接收

文章目录 前言一、阴影接受的步骤1、在v2f中添加UNITY_SHADOW_COORDS(idx),unity会自动声明一个叫_ShadowCoord的float4变量&#xff0c;用作阴影的采样坐标.2、在顶点着色器中添加TRANSFER_SHADOW(o)&#xff0c;用于将上面定义的_ShadowCoord纹理采样坐标变换到相应的屏幕空间…

DC-4 靶机

DC_4 信息搜集 存活检测 详细扫描 后台网页扫描 网页信息搜集 只有一个登陆界面 漏洞利用 尝试使用 burpsuite 密码爆破 尝试使用用户名 admin 登录管理员页面 成功爆破出密码 happy 登录管理员页面 显示可以使用命令 但只能使用三个命令 继续使用 bp 拦截查看数据包…

【C++】VS2019,关于scanf等的报错及其解决方案

参考资料&#xff1a;B站袁春旭老师的网课 报错一&#xff1a;this function may be unsafe. Consider using scanf_s instead. 如下图 这种错误是因为SDL检查不通过&#xff0c;默认这个检查是开的&#xff0c;如下图&#xff0c; 解决方案&#xff1a;把这个SDL检查关闭即…

5、k8s部署Nginx Proxy Manager

前言 Nginx-Proxy-Manager 是一个基于 Web 的 Nginx 服务器管理工具&#xff0c;它允许用户通过浏览器界面轻松地管理和监控 Nginx 服务器。通过 Nginx-Proxy-Manager&#xff0c;可以获得受信任的 SSL 证书&#xff0c;并通过单独的配置、自定义和入侵保护来管理多个代理。用…

51单片机KeyWard

eg1&#xff1a; 单片机键盘的分类 键盘分为编码键盘和非编码键盘&#xff0c;键盘上闭合键的识别由专用的硬件编码器实现&#xff0c;并产生键编码号或键值得称为编码键盘&#xff0c;如计算机键盘&#xff0c;而靠软件来识别的称为非编码键盘&#xff0c;在单片机组成的各种…

Python数字类型

目录 目标 版本 种类 官方文档 数据运算方法 常用函数 转整数 转浮点数 转绝对值 四舍五入 进制转换 math模块常用函数 目标 掌握Python两种数据类型的使用方法。 版本 Python 3.12.0 种类 数字类型有三种&#xff0c;分别是&#xff1a; 整数&#xff08;int&…

C++类和对象(五) 拷贝构造函数

1 概念 在现实生活中&#xff0c;可能存在一个与你一样的自己&#xff0c;我们称其为双胞胎。那在创建对象时&#xff0c;可否创建一个与已存在对象一某一样的新对象呢&#xff1f; 那在创建对象时&#xff0c;可否创建一个与已存在对象一某一样的新对象呢&#xff1f; 拷贝构造…

Linux高性能编程学习-TCP/IP协议族

一、TCP/IP协议族结构与主要协议 分层&#xff1a;数据链路层、网络层、传输层、应用层 1. 数据链路层 功能&#xff1a;实现网卡驱动程序&#xff0c;处理数据在不同物理介质的传输 协议&#xff1a; ARP&#xff1a;将目标机器的IP地址转成MAC地址RARP&#xff1a;将MAC地…

【Java基础面试四十五】、 介绍一下泛型擦除

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 面试官&#xff1a;介绍一下泛型擦除 参考…

2023年10月小程序云开发cms内容管理无法使用,无法同步内容模型到云开发数据库的解决方案

一&#xff0c;问题描述 最近越来越多的同学找石头哥&#xff0c;说cms用不了&#xff0c;其实是小程序官方最近又搞大动作了&#xff0c;偷偷的升级的云开发cms&#xff08;内容管理&#xff09;以下都称cms&#xff0c;不升级不要紧&#xff0c;这一升级&#xff0c;就导致我…

【Matlab】三维绘图函数汇总

本文用于汇总 Matlab 中的三维绘图函数。plot3() 函数用于绘制用参数方程表示的三维曲线。ezplot3() 函数用于三维曲线的符号绘图&#xff0c;需要用参数方程表示。mesh() 函数用于绘制三维曲面网格。surf() 函数用于绘制三维空间曲面。 目录 1. plot3() 2. ezplot3() 3. me…

中医理疗中药材经营小程序商城的作用是什么

对消费者而言&#xff0c;能找到一家靠谱的中医养生馆很重要&#xff0c;但如非朋友介绍或恰好看到纸质广告等&#xff0c;仅依赖线下跑腿很难找到合适的门店&#xff0c;同时也很费时费力。 同时购买相关产品&#xff0c;也只能前往实体店购买或线上第三方商城&#xff0c;当…

【29】c++设计模式——>策略模式

策略模式 C中的策略模式&#xff08;Strategy Pattern&#xff09;是一种行为型设计模式&#xff0c;它允许在运行时选择算法的行为。策略模式通过将算法封装成独立的类&#xff0c;并且使它们可以互相替换&#xff0c;从而使得算法的变化独立于使用算法的客户端。 策略模式通…

2.3.C++项目:网络版五子棋对战之实用工具类模块的设计

文章目录 一、实用工具类模块&#xff08;一&#xff09;功能 二、设计和封装&#xff08;一&#xff09;日志宏封装&#xff08;二&#xff09;mysql_util封装&#xff08;三&#xff09;Jsoncpp-API封装&#xff08;四&#xff09;file_util封装&#xff08;五&#xff09;st…

Linux:实用操作

Linux&#xff1a;实用操作 1. 各类小技巧1.1 controlc(ctrl c) 强制停止1.2 可以通过快捷键&#xff1a;control d(ctrl d)&#xff0c;退出账户的登录1.3 历史命令搜索1.4 光标移动快捷键 2. 软件安装2.1 介绍2.2 yum命令(需要root权限)在这里插入图片描述 3. systemctl4.…

【C++11】函数的可变参数模板

文章目录 一. 为什么要有可变参数模板&#xff1f;二. 什么是可变参数模板&#xff1f;三. 如何展开参数包&#xff1f;1. 递归函数方式展开参数包2. 通过初始化列表展开参数包 一. 为什么要有可变参数模板&#xff1f; C98/03 中的模板为能够实现泛型编程提供了便利&#xff…