Spring AOP的使用
打印接口输入输出日志
定义计算服务类CalculatorService
package com.spring.study.aop.service;
public class CalculatorService {
public int div(int a, int b) {
return a / b;
}
}
定义LogAspect拦截类,并添加@Aspect注解
@Aspect
public class LogAspect {
@Pointcut("execution(public * com.spring.study.aop.aspect.service..*.*(..))")
public void pointCut() {
}
@Before("pointCut()")
public void logStart(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
Object target = joinPoint.getTarget();
Object[] args = joinPoint.getArgs();
System.out.println(target.getClass().getName() + "#" + methodName + " request args : " + Arrays.asList(args));
}
@After("pointCut()")
public void logEnd(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
Object target = joinPoint.getTarget();
long end = System.currentTimeMillis();
}
@AfterReturning(pointcut = "pointCut()", returning = "result")
public void logReturn(JoinPoint joinPoint, Object result) {
String methodName = joinPoint.getSignature().getName();
Object target = joinPoint.getTarget();
System.out.println(target.getClass().getName() + "#" + methodName + " result = " + result);
}
@AfterThrowing(pointcut = "pointCut()", throwing = "exception")
public void logException(Exception exception) {
System.out.println("log logException ex = " + exception);
}
@Around("pointCut()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("logAround start !!!");
Object result = joinPoint.proceed();
System.out.println("logAround end !!!");
return result;
}
}
定义配置类AppConfig,并添加@EnableAspectJAutoProxy注解,用于开启AOP代理自动配置。
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
@Bean
public LogAspect logAspect(){
return new LogAspect();
}
@Bean
public CalculatorService calculatorService(){
return new CalculatorService();
}
}
编写启动的测试类AppMain,从容器中获取CalculatorService,并执行方法div
public class AppMain {
public static void main(String[] args){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
CalculatorService calculatorService = context.getBean(CalculatorService.class);
calculatorService.div(4,2);
context.close();
}
}
运行AppMain类,打印的测试结果如下
logAround start !!!
com.spring.study.aop.aspect.service.CalculatorService#div request args : [4, 2]
logAround end !!!
com.spring.study.aop.aspect.service.CalculatorService#div result = 2
使用注解监控接口性能
定义一个监控注解类Monitor
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface Monitor {
}
编写监控切面类MonitorAspect,拦截添加@Monitor注解的方法,如下
@Aspect
public class MonitorAspect {
@Pointcut("@annotation(com.spring.study.aop.monitor.annotation.Monitor)")
public void pointCut() {
}
@Around("pointCut()")
public Object monitorAround(ProceedingJoinPoint joinPoint) {
long startTime = System.currentTimeMillis();
Object target = joinPoint.getTarget();
String methodName = joinPoint.getSignature().getName();
Object result = null;
try {
result = joinPoint.proceed();
} catch (Throwable e) {
throw new RuntimeException(e.getMessage());
} finally {
System.out.println(target.getClass().getName() + "#" + methodName + " cost time = " + (System.currentTimeMillis() - startTime) + "ms .");
}
return result;
}
}
定义一个OrderService类,并在方法createOrder上添加@Monitor注解
public class OrderService {
@Monitor
public void createOrder(){
try {
Thread.sleep(3000);
}catch (Exception e){
}
}
}
定义系统配置类AppConfig,并添加@EnableAspectJAutoProxy注解,用于开启AOP代理自动配置。
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
@Bean
public MonitorAspect monitorAspect(){
MonitorAspect monitorAspect = new MonitorAspect();
return monitorAspect;
}
@Bean
public OrderService orderService(){
return new OrderService();
}
}
编写启动测试类
public class AppMain {
public static void main(String[] args){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
context.getBean(OrderService.class).createOrder();
context.close();
}
}
打印运行结果如下:
com.spring.study.aop.monitor.annotation.service.OrderService#createOrder cost time = 3015ms .
自定义性能监控的组件
我们的目标是创建一个monitor的监控项目,可以在其他项目中方便的使用。引入monitor.jar 和 在方法上添加注解,就可以达到打印接口日志和监控接口性能。
首先我们创建一个monitor的项目,POM文件如下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.demo.monitor</groupId>
<artifactId>monitor</artifactId>
<packaging>jar</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>monitor</name>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
</dependencies>
</project>
项目的目录结构如图
在工程中创建@Log注解 和 LogMethodInterceptor方法拦截器
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface Log {
}
定义方法拦截器类LogMethodInterceptor
public class LogMethodInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Object target = invocation.getThis();
String methodName = invocation.getMethod().getName();
Object result = null;
String prefix = target.getClass() + "#" + methodName ;
long startTime = System.currentTimeMillis();
try{
System.out.println(prefix + ".request=" + Arrays.asList(invocation.getArguments()));
result = invocation.proceed();
}catch (Exception ex){
}finally {
System.out.println(prefix + ".result=[" + result.toString() +"] cost time : " + (System.currentTimeMillis() - startTime) + "ms");
}
return result;
}
}
定义日志管理的配置类LogManagementConfiguration,用于创建通知,切入点 和拦截器。
@Configuration
public class LogManagementConfiguration {
@Bean
public DefaultPointcutAdvisor logAdvisor() {
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
advisor.setAdvice(logMethodInterceptor());
advisor.setPointcut(new StaticMethodMatcherPointcut() {
@Override
public boolean matches(Method method, Class<?> aClass) {
return AnnotationUtils.findAnnotation(method, Log.class) != null;
}
});
return advisor;
}
@Bean
public LogMethodInterceptor logMethodInterceptor() {
LogMethodInterceptor interceptor = new LogMethodInterceptor();
return interceptor;
}
}
如何自动创建LogManagementConfiguration实例呢,我们定义一个LogManagementConfigurationSelector类,让它实现ImportSelector接口的selectImports方法,返回LogManagementConfiguration对象的类名。
public class LogManagementConfigurationSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{LogManagementConfiguration.class.getName()};
}
}
我们模仿@EnableTransactionManagement注解的定义,创建一个EnableLogManagement注解,添加@Import(LogManagementConfigurationSelector.class)
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(LogManagementConfigurationSelector.class)
public @interface EnableLogManagement {
}
好了,我们的准备工作已经做完了,那么怎么在项目中使用呢?很简单我们只需引入maven的坐标,并在需要监控的方法上添加 @Monitor注解就可以 了。
<dependency>
<groupId>com.demo.monitor</groupId>
<artifactId>monitor</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
定义AppConfig配置类,在配置类中添加@EnableLogManagement注解
@Configuration
@EnableLogManagement
@EnableAspectJAutoProxy
public class AppConfig {
@Bean
public OrderService orderService(){
return new OrderService();
}
}
编写启动测试类,用于测试我们开发的组件
public class AppMain {
public static void main(String[] args){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
context.getBean(OrderService.class).createOrder("yyp",200);
context.close();
}
}
打印的运行日志如下:
class com.spring.study.aop.log.OrderService#createOrder.request=[yyp, 200]
class com.spring.study.aop.log.OrderService#createOrder.result=[OK] cost time : 12ms