Spring AOP
这里是引用
一,介绍
spring aop工作原理图
1.什么是spring aop
Spring AOP(Aspect-Oriented Programming)是Spring框架提供的一种面向切面编程的技术。它通过将横切关注点(例如日志记录、事务管理、安全性检查等)从主业务逻辑代码中分离出来,以模块化的方式实现对这些关注点的管理和重用。
在Spring AOP中,切面(Aspect)是一个模块化的关注点,它可以跨越多个对象,例如日志记录、事务管理等。切面通过定义切点(Pointcut)和增强(Advice)来介入目标对象的方法执行过程。
切点是一个表达式,用于匹配目标对象的一组方法,在这些方法执行时切面会被触发。增强则定义了切面在目标对象方法执行前、执行后或抛出异常时所要执行的逻辑。
Spring AOP提供了以下几种类型的增强:
- 前置增强(Before Advice):在目标方法执行之前执行的逻辑。
- 后置增强(After Advice):在目标方法执行之后执行的逻辑,不管目标方法是否抛出异常。
- 返回增强(After Returning Advice):在目标方法正常返回时执行的逻辑。
- 异常增强(After Throwing Advice):在目标方法抛出异常时执行的逻辑。
- 环绕增强(Around Advice):在目标方法执行前后都可以执行的逻辑,它可以完全控制目标方法的执行。
Spring AOP通过使用动态代理技术,在目标对象方法执行时将切面的逻辑织入到目标对象的方法中。这样,我们可以在不修改原始业务代码的情况下,实现横切关注点的统一处理。
总而言之,Spring AOP是一种通过切面将横切关注点模块化的技术,它提供了一种简洁的方式来管理和重用跨越多个对象的关注点逻辑。
2.为什么要用spring aop
使用Spring AOP的主要原因是它可以帮助我们更好地管理各种横切关注点,例如日志记录、事务管理、安全性检查等。以下是一些使用Spring AOP的优点:
-
模块化:Spring AOP将横切关注点从主业务逻辑代码中分离出来,以模块化的方式实现对这些关注点的管理和重用。这样,我们可以更容易地维护代码,并且可以将同一个关注点的逻辑应用到多个方法或类中。
-
非侵入式:使用Spring AOP时,我们不需要修改原始业务逻辑代码,只需要在切点和增强中定义我们所需要的逻辑即可。这样,我们可以保持原始代码的简洁性和可读性。
-
可重用性:我们可以将同一个切面应用于多个目标对象进行横切处理。这样,我们可以提高代码的重用性,并且可以更加方便地维护和更新切面逻辑。
-
松耦合:AOP可以减少各个业务模块之间的耦合度,这是因为我们可以将某些通用的逻辑作为切面来实现,而不是直接在各个业务模块中实现。这样可以使得各个业务模块之间更加独立,从而提高代码的可维护性。
总之,使用Spring AOP可以帮助我们更好地管理和重用横切关注点逻辑,使得代码更易于维护和理解,并且可以提高代码的可重用性和灵活性。
3.spring aop的好处
Spring AOP(Aspect-Oriented Programming)是一种编程范式,它通过切面(Aspect)的概念来提供对横切关注点的支持。在传统的面向对象编程中,我们将功能逻辑以对象的形式组织起来,而在面对特定需求时,常常需要在多个对象或方法中添加相同的功能代码,例如日志记录、事务管理等。这样的代码重复不仅增加了代码量,也使得代码难以维护和理解。
Spring AOP的目标是通过将横切关注点与主业务逻辑进行解耦,实现关注点的模块化和可重用性。横切关注点指的是与业务逻辑无关但又必须在多个地方进行处理的功能,如日志记录、事务管理、异常处理等。
在Spring AOP中,我们可以定义切面(Aspect),切面由切点(Pointcut)、通知(Advice)和连接点(Joinpoint)组成。切点定义了哪些连接点会被切面所影响,通知定义了在切点处执行的逻辑,而连接点则表示程序执行过程中的某个特定点。
Spring AOP的工作原理是通过动态代理的方式,在运行时将切面逻辑织入到目标对象的方法中,从而实现对横切关注点的处理。
使用Spring AOP的好处包括:
- 模块化:将横切关注点从业务逻辑中分离出来,以模块化的方式进行管理。
- 非侵入性:不需要修改原始业务逻辑代码,只需在切面中定义关注点的逻辑。
- 可重用性:将同一切面应用于多个目标对象,提高代码的可重用性。
- 松耦合:减少各业务模块之间的耦合度,提高代码的可维护性。
- 提高系统性能和可扩展性:通过减少重复代码和逻辑,提高系统性能和可扩展性。
总结而言,Spring AOP通过切面的概念,实现了对横切关注点的解耦和重用,提高了代码的组织性、可维护性和可扩展性。
4.核心概念
是的,Spring AOP 的核心概念包括切面、连接点、通知、切点以及织入。下面我将对这些概念做一些简要的解释:
-
切面(Aspect):切面是一个模块化的横切关注点实现,它包括了连接点和通知。可以通过配置文件、注解等方式定义切面。
-
连接点(Joinpoint):程序中能够被切面插入的点,典型的连接点包括方法调用、方法执行过程中的某个时点等等。
-
通知(Advice):在连接点处执行的代码。通知分为各种类型,如前置通知、后置通知、环绕通知等。
-
切点(Pointcut):用于定义哪些连接点上应该应用通知。切点通过表达式进行定义,如匹配所有 public 方法或匹配某个包下的所有方法等。
-
织入(Weaving):指将切面应用到目标对象并创建新的代理对象的过程。织入可以在运行时完成,也可以在编译时完成。 Spring AOP 提供了两种织入方式:编译期织入和运行期织入。
除此之外,Spring AOP 还有其他常用的概念,如目标对象(Target)、代理对象(Proxy)等。目标对象是含有连接点的对象,而代理对象是 Spring AOP 创建的一个包含切面代码的对象。
以上就是 Spring AOP 的核心概念,它们共同构成了切面编程的基础。
5.spring aop JDK动态代理和cglib动态代理的区别
Spring AOP 支持两种类型的动态代理:JDK 动态代理和 CGLIB 动态代理。它们之间有以下区别:
-
基于类型:JDK 动态代理是基于接口的代理,而 CGLIB 动态代理是基于类的代理。
-
代理对象创建:JDK 动态代理通过 Java 自带的 java.lang.reflect.Proxy 创建代理对象,该对象必须实现一个或多个接口。CGLIB 动态代理通过字节码生成技术创建代理对象,无需目标类实现接口,直接继承目标类。
-
性能:JDK 动态代理在运行时需要使用反射,导致较低的性能。CGLIB 动态代理通过生成字节码,避免了反射,因此通常比 JDK 动态代理速度更快。
-
对象类型:JDK 动态代理只能代理具有接口的目标对象,不适用于没有接口的类。CGLIB 动态代理可以代理任何类,包括没有实现接口的类。
-
继承:JDK 动态代理只能代理目标对象的接口方法,不能代理其父类中的方法。CGLIB 动态代理可以代理目标类及其父类中的方法。
综上所述,选择使用 JDK 动态代理还是 CGLIB 动态代理取决于具体的需求和场景。如果目标对象实现了接口并且对性能要求较高,可以选择 JDK 动态代理;如果目标对象没有实现接口或者对性能要求不那么苛刻,可以选择 CGLIB 动态代理。默认情况下,Spring AOP 使用 JDK 动态代理,但在某些情况下会自动切换到 CGLIB 动态代理。
6.aop和oop的区别
AOP (Aspect-Oriented Programming) 和 OOP (Object-Oriented Programming) 都是面向对象编程的范式,但它们关注的角度不同。下面是它们之间的区别:
-
技术视角:OOP 是一种代码组织技术,它通过将数据和操作封装在对象中来实现模块化和复用。AOP 是一种编程范式,它允许开发者在执行过程中(而非编译期)动态地添加、删除或修改代码的功能。
-
关注点:OOP 关注对象的内部结构和行为,其目标是更好地描述和设计一个系统的真实世界概念。AOP 关注横切关注点(Cross-cutting Concerns),即存在于应用程序各个层面的相同问题,如日志、事务、安全等。
-
实现方式:OOP 是通过类和接口来实现封装、继承和多态等特性,使得类能够更好地表达问题领域内的概念。AOP 是通过将通用功能抽取为切面(Aspect),并与核心业务逻辑混合使用,来实现这些横切关注点。
-
关注点分离:OOP 面对复杂系统可能导致代码重复或紧密耦合的问题,而 AOP 采用横切关注点的方式来解决这些问题,使得系统功能更加组合和重用。
综上所述,OOP 是一种面向对象的编程技术,它关注如何将数据和操作组织在一起。而 AOP 通过提取和组合通用功能以及横跨多个层次的共性代码来实现程序逻辑的复用和可维护性的增强。
二,基本使用代码示例
1.spring 版
下面是一个简单的 Spring AOP 的代码示例:
- 创建目标类(Target Class):
public class UserService {
public void addUser(String username) {
System.out.println("Adding user: " + username);
}
}
- 定义切面类(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);
}
}
- 配置 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 示例代码:
- 创建目标类(Target Class):
@Service
public class UserService {
public void addUser(String username) {
System.out.println("Adding user: " + username);
}
}
- 定义切面类(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);
}
}
- 启用 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 来实现此功能。
- 创建
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());
}
}
- 在
HelloService
中添加业务方法:
@Service
public class HelloService {
public String sayHello(String name) {
return "Hello, " + name;
}
}
- 在应用程序启动类中声明 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();
}
}
- 启动应用程序,并调用
HelloService
的sayHello
方法,观察控制台日志输出:
@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 实现事务管理的示例:
- 添加 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>
- 在
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
- 创建一个 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));
}
}
- 创建一个 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;
}
}
- 在应用程序启动类中声明 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 实现日志记录管理的示例:
- 添加 Spring Boot Starter AOP 依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
- 创建一个 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);
}
}
- 配置日志输出格式和级别。我们在
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。
- 在应用程序启动类中声明 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 实现安全控制管理的示例:
- 添加 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>
- 创建一个 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();
}
}
- 创建一个自定义注解
RequiredPermission
,用于标注需要进行权限检查的方法。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiredPermission {
String value();
}
- 在应用程序启动类中声明 AOP 配置。
@SpringBootApplication
@EnableAspectJAutoProxy
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public SecurityAspect securityAspect() {
return new SecurityAspect();
}
}
- 在 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 实现参数校验管理的示例:
- 添加 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>
- 创建一个 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);
}
}
}
- 在 Service 层的方法参数上使用标准的 Java Bean Validation 注解进行参数校验。例如,使用
@NotNull
、@Min
、@Max
等注解。
@Service
public class UserService {
public void addUser(@NotNull String name, @Min(18) @Max(99) Integer age) {
// 添加用户的逻辑
}
}
- 在应用程序启动类中声明 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 实现性能监控的示例:
- 添加 Spring Boot Starter AOP 依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
- 创建一个 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;
}
}
- 在应用程序启动类中声明 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 实现数据缓存的示例:
- 添加 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>
- 配置 Redis 的连接信息,在
application.properties
或application.yml
文件中添加以下配置:
spring.redis.host=127.0.0.1
spring.redis.port=6379
- 创建一个 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;
}
}
- 在应用程序启动类中配置 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 缓存中获取数据。如果找到了缓存数据,则直接返回;如果没有找到,则执行方法逻辑并将结果存入缓存。这样可以提高数据读取的性能,减少对数据源的访问。需要注意的是,缓存的键需要保证唯一性,不同的方法和参数应该使用不同的缓存键。