springBoot,AOP切面实现日志记录,自定义注解,注解属性动态传参 注解属性动态传参,包括:方法入参传参、方法体传参 方法入参传参,针对链接携带的参数进行记录 方法体传参,对处理后的数据进行记录
1、遇到的问题
1)Caused by: java.lang.IllegalArgumentException: error at ::0 formal unbound in pointcut
解决方案 ,把两个入参的位置换一下就好了
2、自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogAnnotation {
String operation() default "";
String description() default "";
}
JDK中有一些元注解,主要有@Target,@Retention,@Document,@Inherited用来修饰注解。 @Target 表明该注解可以应用的java元素类型
Target类型 | 描述 |
---|
ElementType.TYPE | 应用于类、接口(包括注解类型)、枚举 | ElementType.TYPE | 应用于类、接口(包括注解类型)、枚举 | ElementType.FIELD | 应用于属性(包括枚举中的常量) | ElementType.METHOD | 应用于方法 | ElementType.PARAMETER | 应用于方法的形参 | ElementType.CONSTRUCTOR | 应用于构造函数 | ElementType.LOCAL_VARIABLE | 应用于局部变量 | ElementType.ANNOTATION_TYPE | 应用于注解类型 | ElementType.PACKAGE | 应用于包 | ElementType.TYPE_PARAMETER | 1.8版本新增,应用于类型变量) | ElementType.TYPE_USE | 1.8版本新增,应用于任何使用类型的语句中(例如声明语句、泛型和强制转换语句中的类型) |
@Retention 表明该注解的生命周期
生命周期类型 | 描述 |
---|
RetentionPolicy.SOURCE | 编译时被丢弃,不包含在类文件中 | RetentionPolicy.CLASS | JVM加载时被丢弃,包含在类文件中,默认值 | RetentionPolicy.RUNTIME | 由JVM 加载,包含在类文件中,在运行时可以被获取到 |
@Document 表明该注解标记的元素可以被Javadoc 或类似的工具文档化 @Inherited 表明使用了@Inherited注解的注解,所标记的类的子类也会拥有这个注解
3、aop切面(aspect)
1) 静态参数
①、切面代码
package com.meirit.dong.aspect;
import com.meirit.dong.annotation.LogAnnotation;
import com.meirit.dong.entity.LogEntity;
import com.meirit.dong.mapper.LogMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
@Slf4j
@Aspect
@Component
public class LogAspect {
@Autowired
private LogMapper logMapper;
@Pointcut("@annotation(logger)")
public void logAnnotation(LogAnnotation logger) {
}
@Around("logAnnotation(logger)")
public Object around(ProceedingJoinPoint joinPoint, LogAnnotation logger) throws Throwable {
Object[] args = joinPoint.getArgs();
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
saveLog(method);
Class[] paramTypeArray = methodSignature.getParameterTypes();
args[1] = 30;
Object result = joinPoint.proceed(args);
return result;
}
private void saveLog(Method method) {
LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class);
LogEntity logEntity = new LogEntity();
logEntity.setId(UUID.randomUUID().toString());
logEntity.setOperation(logAnnotation.operation());
logEntity.setDescription(logAnnotation.description());
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
logEntity.setCreateTime(sf.format(new Date()));
logMapper.insertLog(logEntity);
}
}
②、具体应用
@GetMapping("/test1")
@ResponseBody
@LogAnnotation(operation = "查询", description = "查询人员信息")
public String test1(String name, int num) throws IOException {
return "success" + num;
}
③、获取注解内容的2种方式
-
通过入参获取 -
通过自带的属性获取
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class);
2) 动态传参
动态传参的两个核心:
① 获取通配符 ② 获取通配符的值
通配符的定义如下
@GetMapping("/test1")
@ResponseBody
@LogAnnotation(operation = "#{operation}", description = "#{description}")
public String test1(String operation, int num) throws IOException {
AnnotationResolver.INSTANCE.params.put("description","查询人员信息");
return "success" + num;
}
注意:上面代码中,传递参数的方式有2种; 第一种:直接通过入参传递,如operation 第二种:在方法体里面传递,如description
代码实现如下
①、切面代码
package com.meirit.dong.aspect;
import com.meirit.dong.annotation.LogAnnotation;
import com.meirit.dong.aspect.resolver.AnnotationResolver;
import com.meirit.dong.entity.LogEntity;
import com.meirit.dong.mapper.LogMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
@Slf4j
@Aspect
@Component
public class LogAspect {
@Autowired
private LogMapper logMapper;
@Pointcut("@annotation(com.meirit.dong.annotation.LogAnnotation)")
public void logAnnotation() {
}
@Around("logAnnotation()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
Object[] args = joinPoint.getArgs();
args[1] = 30;
Object result = joinPoint.proceed(args);
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
saveLog(method,joinPoint);
return result;
}
private void saveLog(Method method, ProceedingJoinPoint joinPoint) {
LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class);
LogEntity logEntity = new LogEntity();
logEntity.setId(UUID.randomUUID().toString());
Object operation = AnnotationResolver.INSTANCE.resolver(joinPoint, logAnnotation.operation());
Object description = AnnotationResolver.INSTANCE.resolver(joinPoint, logAnnotation.description());
logEntity.setOperation(String.valueOf(operation));
logEntity.setDescription(String.valueOf(description));
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
logEntity.setCreateTime(sf.format(new Date()));
logMapper.insertLog(logEntity);
}
}
② 解析通配符代码
package com.meirit.dong.aspect.resolver;
import graphql.com.google.common.collect.Maps;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import java.lang.reflect.Method;
import java.util.Map;
public enum AnnotationResolver {
INSTANCE;
public Map<String,Object> params = Maps.newConcurrentMap();
public Object resolver(JoinPoint joinPoint, String str) {
if (str == null) {
return null ;
}
Object value = null;
if (str.matches("#\\{\\D*\\}")) {
String newStr = str.replaceAll("#\\{", "").replaceAll("\\}", "");
if (newStr.contains(".")) {
try {
value = complexResolver(joinPoint, newStr);
} catch (Exception e) {
e.printStackTrace();
}
} else {
value = simpleResolver(joinPoint, newStr);
}
} else {
value = str;
}
return value;
}
private Object complexResolver(JoinPoint joinPoint, String str) throws Exception {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
String[] names = methodSignature.getParameterNames();
Object[] args = joinPoint.getArgs();
String[] strs = str.split("\\.");
for (int i = 0; i < names.length; i++) {
if (strs[0].equals(names[i])) {
Object obj = args[i];
Method dmethod = obj.getClass().getDeclaredMethod(getMethodName(strs[1]), null);
Object value = dmethod.invoke(args[i]);
return getValue(value, 1, strs);
}
}
return null;
}
private Object getValue(Object obj, int index, String[] strs) {
try {
if (obj != null && index < strs.length - 1) {
Method method = obj.getClass().getDeclaredMethod(getMethodName(strs[index + 1]), null);
obj = method.invoke(obj);
getValue(obj, index + 1, strs);
}
return obj;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private String getMethodName(String name) {
return "get" + name.replaceFirst(name.substring(0, 1), name.substring(0, 1).toUpperCase());
}
private Object simpleResolver(JoinPoint joinPoint, String str) {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
String[] names = methodSignature.getParameterNames();
Object[] args = joinPoint.getArgs();
Object value = null;
if(params.keySet().contains(str)){
value = params.get(str);
params.remove(str);
}else {
for (int i = 0; i < names.length; i++) {
if (str.equals(names[i])) {
value = args[i];
break;
}
}
}
return value;
}
}
③、业务实现
@GetMapping("/test1")
@ResponseBody
@LogAnnotation(operation = "#{operation}", description = "#{description}")
public String test1(String operation, int num) throws IOException {
AnnotationResolver.INSTANCE.params.put("description","查询人员信息");
return "success" + num;
}
|