每次想写切面时都会忘记细节,只能去翻以前的代码,很尴尬,在这里记录一下大致的使用。
依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.2</version>
</dependency>
// 1、定义一个切面类,定义为组件,使用@Component和@Aspect注解
@Component
@Aspect
// @Order定义优先级,值越小优先级越高
@Order(Ordered.HIGHEST_PRECEDENCE)
public class AnnontationAdvice {
/*
有五种Advice,可以在切点上进行增强处理
@Before : 在切点方法之前执行
@After : 在切点方法之后执行
@AfterReturning : 切点方法返回后执行,可以对返回值进行修饰
@Around : 环绕通知,能控制切点执行前,执行后,甚至👎点函数是否要执行
@AfterThrowing :切点方法抛异常执行
*/
// 2、定义切点,获取输入参数,也可以从JoinPoint中获取入参等详细信息,具体看下一个代码块
@Before("execution(* com.jenson.chapter4.app.server.IAOPService.withAop*(..)) && args(name,age)")
//3、如果切点为某个注解(@EnableHumpField)的函数,通常这种情况比较多
// @Before("@annotation(com.jenson.chapter4.infra.annotation.EnableHumpField)")
public void beforeAdvice(String name, Long age) {
System.out.println("前置通知执行了 name:"+name+" age:"+age);
}
@After("execution(* com.jenson.chapter4.app.server.IAOPService.withAop*(..))")
public void afterAdvice() {
System.out.println("后置通知执行了");
}
@AfterReturning(value = "execution(* com.jenson.chapter4.app.server.IAOPService.withAop*(..))", returning = "result")
public void afterReturnAdvice(JoinPoint joinPoint, String result) {
System.out.println("返回通知执行了" + "运行业务方法放回结果为" + result);
return (result + "==被修饰过1");
}
@Around("execution(* com.jenson.chapter4.app.server.IAOPService.withAop*(..))")
public String arondAdvvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
String result = "";
try {
System.out.println("环绕通知开始执行了aaaa");
long start = System.currentTimeMillis();
result = (String) proceedingJoinPoint.proceed();
long end = System.currentTimeMillis();
System.out.println("环绕通知执行结束了bbbb");
System.out.println("执行业务方法共计:" + (end - start) + "毫秒。");
} catch (Throwable e) {
}
return (result + "==被修饰过2");
}
@AfterThrowing(value = "execution(* com.jenson.chapter4.app.server.IAOPService.withAop*(..))", throwing = "e")
public void throwingAdvice(JoinPoint joinPoint, Exception e) {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("异常通知执行了.");
stringBuffer.append("方法:").append(joinPoint.getSignature());
System.out.println(stringBuffer.toString());
}
}
从JoinPoint中获取入参等详细信息
// 切点为一个注解
@Before("@annotation(com.alm.ori.infra.annotation.ThisIsAnnotation)")
public void beforeAdvice(JoinPoint joinPoint) {
// 获取函数
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
// 获取所有注解
Annotation[] annotations = methodSignature.getMethod().getAnnotations();
// 获取某个注解(@RequestMapping)的信息
joinPoint.getTarget().getClass().getAnnotation(RequestMapping.class);
// 获取参数名
String[] parameterNames = methodSignature.getParameterNames();
// 获取参数
Object[] objs = joinPoint.getArgs();
}
使用AOP写一个记录api日志的功能
参考:https://github.com/xkcoding/spring-boot-demo/tree/master/demo-log-aop
/**
* 接口调用日志
*
* @author Jenson
*/
@Aspect
@Component
@Slf4j
public class ApiLogAspect {
@AfterReturning(value = "execution(* com.jenson.something.api.controller.*.*Controller.*(..))", returning = "result")
public void afterReturningLog(JoinPoint joinPoint, Object result) {
try {
final ApiLog l = this.generateApiLog(joinPoint, result);
log.debug("Request Log Info : {}", JSON.toJSONString(l, SerializerFeature.DisableCircularReferenceDetect));
} catch (Exception e) {
log.error(e.getMessage());
}
}
/**
* 生成Api日志
*
* @param joinPoint 切入点
* @param result 返回值
* @return api日志
*/
private ApiLog generateApiLog(JoinPoint joinPoint, Object result) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();
StringBuffer requestURL = request.getRequestURL();
String requestMethod = request.getMethod();
String currentThreadName = Thread.currentThread().getName();
long currentThreadId = Thread.currentThread().getId();
return ApiLog.builder()
.requestUrl(requestURL.toString())
.requestType(requestMethod)
.threadId(String.valueOf(currentThreadId))
.threadName(currentThreadName)
.classMethod(String.format("%s.%s", joinPoint.getSignature().getDeclaringTypeName(),
joinPoint.getSignature().getName()))
.requestParams(this.getNameAndValue(joinPoint))
.result(result)
.build();
}
/**
* 获取方法参数名和参数值
*
* @param joinPoint 切入点
* @return 请求参数
*/
private Map<String, Object> getNameAndValue(JoinPoint joinPoint) {
final Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
final String[] names = methodSignature.getParameterNames();
final Object[] args = joinPoint.getArgs();
if (ArrayUtil.isEmpty(names) || ArrayUtil.isEmpty(args)) {
return Collections.emptyMap();
}
if (names.length != args.length) {
log.warn("{}方法参数名和参数值数量不一致", methodSignature.getName());
return Collections.emptyMap();
}
Map<String, Object> map = Maps.newHashMap();
for (int i = 0; i < names.length; i++) {
map.put(names[i], args[i]);
}
return map;
}
}
/**
* 进入接口时日志记录对象
*
* @author Jenson
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ApiLog {
/**
* 请求Url
*/
private String requestUrl;
/**
* 请求方式
*/
private String requestType;
/**
* 线程id
*/
private String threadId;
/**
* 线程号
*/
private String threadName;
/**
* 类方法
*/
private String classMethod;
/**
* 请求参数
*/
private Object requestParams;
/**
* 返回参数
*/
private Object result;
}
网友评论