AOP的用途
除了日志记录外,AOP还可以用于以下情况:
缓存管理:通过AOP,可以将缓存逻辑从业务逻辑中分离出来,实现缓存的统一管理和更高效的数据读取。例如,使用AOP实现缓存切面,可以在方法执行前检查缓存中是否有数据,如果有,则直接从缓存中读取数据并返回,否则执行方法并将结果存入缓存中。
权限控制:通过AOP,可以将权限控制逻辑从业务逻辑中分离出来,实现统一的权限管理和更好的安全性。例如,使用AOP实现权限控制切面,可以在方法执行前检查用户的权限,如果有权限,则允许执行方法,否则拒绝执行。
性能监控:通过AOP,可以实现统一的性能监控和更好的性能优化。例如,使用AOP实现性能监控切面,可以在方法执行前记录当前时间戳,并在方法执行后计算方法执行时间,并将结果输出到日志中,从而帮助开发人员识别慢速代码并进行优化。
异常处理:通过AOP,可以将异常处理逻辑从业务逻辑中分离出来,实现统一的异常处理和更好的代码健壮性。例如,使用AOP实现异常处理切面,可以在方法执行过程中检查异常并进行相应的处理,例如将异常信息记录到日志中、发送邮件通知等。
缓存的例子
// 定义缓存注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Cached {
String value();
}
// 定义缓存切面
@Aspect
@Component
public class CacheAspect {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Around("@annotation(cached)")
public Object cache(ProceedingJoinPoint joinPoint, Cached cached) throws Throwable {
String key = cached.value();
Object result = redisTemplate.opsForValue().get(key);
if (result != null) {
return result;
}
result = joinPoint.proceed();
redisTemplate.opsForValue().set(key, result);
return result;
}
}
// 使用缓存注解
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Cached("userCache")
@Override
public User getUserById(int id) {
return userDao.getUserById(id);
}
}
在这个例子中,我们定义了一个@Cached注解,用于标记需要缓存的方法。然后,我们实现了一个缓存切面,使用@Around注解将切面应用到@Cached注解标记的方法上。在缓存切面中,我们首先检查缓存中是否存在该数据,如果存在,则直接从缓存中读取并返回,否则执行该方法并将结果存入缓存中。最后,我们在UserServiceImpl类的getUserById方法上使用@Cached注解标记该方法需要缓存。
这个例子中使用了Redis作为缓存存储,当然也可以使用其他缓存方案,例如Ehcache、Memcached等。
性能控制的例子
添加依赖
在pom.xml文件中添加以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
上述依赖中包含Micrometer核心库和Prometheus度量系统的集成库。
定义一个切面类,使用@Aspect注解表示这是一个切面,使用@Component注解将其注册为Spring Bean。
配置Micrometer
然后,在你的Spring Boot应用程序中,你可以添加以下配置类来创建MeterRegistry bean:
@Configuration
public class MetricsConfig {
@Bean
public MeterRegistry meterRegistry() {
return new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
}
}
这个配置类使用PrometheusMeterRegistry来创建一个MeterRegistry bean,并使用默认的PrometheusConfig配置。当你运行你的应用程序时,你应该可以看到指标被收集并发布到Prometheus中了。
在application.yml文件中添加Micrometer相关配置:
management:
metrics:
tags:
app: myapp
export:
prometheus:
enabled: true
这里配置了app标签,用于标识应用程序,以及启用了Prometheus导出器。
定义切面
package com.wg.myAspect;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.time.Duration;
/**
*
* @author lst
* @date 2023/4/18 10:54
* @return null
*/
@Aspect
@Component
public class PerformanceAspect {
private static final Logger logger = LoggerFactory.getLogger(PerformanceAspect.class);
private final Timer timer;
public PerformanceAspect(MeterRegistry registry) {
this.timer = registry.timer("app.service.method.execution.time");
}
// @Around("execution(* com.wg.controller..*.*(..))")
@Around("execution(* com.wg.controller.UserController.*(..))")
public Object monitor(ProceedingJoinPoint joinPoint) throws Throwable {
Timer.Sample sample = Timer.start();
Object result = joinPoint.proceed();
long durationNanos = sample.stop(this.timer);
Duration duration = Duration.ofNanos(durationNanos);
long durationSeconds = duration.toSeconds();
String methodName = joinPoint.getSignature().getName();
String className = joinPoint.getTarget().getClass().getSimpleName();
logger.info("Method {} in class {} took {} seconds to execute", methodName, className, durationSeconds);
return result;
}
}