SpringAOP面向切面编程。基于注解 的Around通知。在Controller实现切面日志功能,修改入参,修改返回值
基于注解 的Around通知。
在Controller实现切面日志功能,修改入参,修改返回值
基于注解 的Around通知
自定义注解、在需要切面的方法上添加注解,在切面类获取 方法信息
maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
一、代码如下
1.自定义日志注解
/**
* 自定义注解实现 切面日志功能
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Loggable {
boolean isSave() default true;
String ModelName() default "";
}
2.切面类
@Aspect
@Component
@Slf4j
public class LogAspect {
/**
* 定义环绕增强.
*
* 功能1:记录 方法的 修饰符,名称、参数名称、参数值、返回值。方法的类名
* 功能2:修改方法的入参,传入方法执行,最后修改方法的返回值
*/
// @Around("execution(* com.example.demo.controller.*.*(..))")
@Around("@annotation(Loggable)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
//连接点(JoinPoint)的方法签名
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
String clazzName = signature.getDeclaringType().getSimpleName();//类名
String className = signature.getDeclaringTypeName();//类名(全限定类名)
String methodName = signature.getName();//方法名
String methodModify = Modifier.toString(signature.getModifiers());//方法的修饰符(modifier)
log.info("切面:方法基本信息:"+className + " ----- " + clazzName + " " + methodModify + " " + methodName);
//注解处理:获取注解值 来控制切面逻辑,比如 isSave决定是否保存,
Method method = signature.getMethod();
Annotation[] annotations = method.getAnnotations();
for (Annotation annotation : annotations) {
if (annotation instanceof Loggable) {
Loggable myAnnotation = (Loggable) annotation;
boolean isSave= myAnnotation.isSave();
String modelName= myAnnotation.ModelName();
log.info("切面:日志注解的值:isSave ="+isSave+" modelName="+modelName);
break;
}
}
//-------------入参处理
Object[] args = joinPoint.getArgs();//方法参数值
String[] parameterNames = signature.getParameterNames();// 方法参数名称
//入参:遍历参数值和名称
JSONObject param = new JSONObject(true);
for (int i = 0; i < args.length; i++) {
Object argValue = args[i];
String argName = parameterNames[i];
param.put(argName, argValue);
}
log.info("切面:参数 :"+param.toJSONString());
//修改入参
for (int i = 0; i < args.length; i++) {
Object arg = args[i];
//String类型或者Integer等包装类,需要对入参数组进行重新赋值
if (arg instanceof String) {
args[i] = arg + "aop——update";//修改入参,对 String类型的参数 增加"aop——update"
} else if (arg instanceof Integer) {
args[i] = (int) arg + 4;//修改入参,对 int类型的参数 增加4
} else if (arg instanceof Long) {
args[i] = (long) arg + 8;//修改入参,对 long类型的参数 增加8
} else if (arg instanceof Float) {
args[i] = (float) arg + 8;//修改入参,对 float类型的参数 增加8
} else if (arg instanceof Double) {
args[i] = (double) arg + 8;//修改入参,对 double类型的参数 增加8
}
//对象类型的入参:直接修改属性
if (arg instanceof User) {
User params = (User) arg;
params.setId(params.getId() * params.getId());//修改入参,user的id
}
}
//传入修改后的入参,执行方法
Object response = joinPoint.proceed(args);
//修改返回值
if (response instanceof Integer) {
response = (int) response + 100;
}
return response;//方法的返回值是around修改后的返回值,而不是原来方法的返回值
}
}
3.controller
@RestController
@Slf4j
public class ExpertController {
@Resource
MyService myService;
@GetMapping("/aop/annotation/get")
@Loggable(isSave = true,ModelName = "os")
public Integer aopAnnotationGet(
@RequestParam("strings")String strings,
@RequestParam("integers")Integer integers,
@RequestParam("longs")Long longs,
@RequestParam("floats")Float floats,
@RequestParam("doubles")Double doubles){
log.info("GET,多个包装类参数,当前方法使用 annotation 标记连接点");
log.info("参数:strings="+strings+" integers="+integers+" longs="+ longs+" floats="+floats+" doubles="+doubles);
Integer result = myService.doSomethingAnnation(strings,integers,longs,floats,doubles);
log.info("返回值: " + result);
return result;
}
@PostMapping("/aop/annotation/post")
@Loggable(isSave = false,ModelName = "manage")
public Integer aopAnnotationPost(@RequestBody User user){
log.info("POST,1个对象参数,当前方法使用 annotation 标记连接点");
log.info("参数:"+ JSON.toJSONString(user));
Integer result = myService.doSomethingAnnation(user.getId(), user.getName(), 0L);
log.info("返回值: " + result);
return result;
}
}
4.service
@Service
@Slf4j
public class MyService {
public Integer doSomethingAnnation(String strings,
Integer integers,
Long longs,
Float floats,
Double doubles) {
return Math.toIntExact(integers + longs);
}
public Integer doSomethingAnnation(Integer a,String b,Long c) {
return Math.toIntExact(a + c);
}
public Integer doSomething(Integer a,String b,Long c) {
return Math.toIntExact(a + c);
}
}
测试
1.测试GET方法
执行接口请求
http://localhost:6565/aop/annotation/get?strings=example&integers=10&longs=1000&floats=3.14&doubles=2.71828
分析日志:
————进入切面
第一行:切面 获取了 全限定类名, 类名,方法的修饰符,方法名称
第二行:切面 获取了 自定义的日志注解的值。
设置参数值来控制切面的逻辑,比如 isSave决定是否保存日志。
第三行:切面获取了 参数名称和参数值(postman中我们设置的参数值)
————进入接口
第四行:接口打印信息
第五行:接口打印入参。对比第三行,明显不一样,因为【切面修改了入参】
我们定义的修改规则是 字符串加后缀,数字加自身类型的字节数
第六行:接口打印返回值1022。1022=14+1008
接口返回结果 :1122 ,对比第六行,多了100,因为【切面中修改了 返回值】
我们定义的修改规则是 如果是Integer类型的返回值,则再加100返回
测试结果
- 成功获取方法的基本信息(类名、方法名、入参)
- 成功获取了注解
- 成功修改了入参
- 成功修改了返回值
2.执行POST方法
执行接口请求
http://127.0.0.1:6565/aop/annotation/post
{ “id”: “4”,
“name”: “ww”,
“status”: true
}
分析日志:
————进入切面
第一行:切面 获取了 全限定类名, 类名,方法的修饰符,方法名称
第二行:切面 获取了 自定义的日志注解的值。
设置参数值来控制切面的逻辑,比如 isSave决定是否保存日志。
第三行:切面获取了 参数名称和参数值(postman中我们设置的参数值)
————进入接口
第四行:接口打印信息
第五行:接口打印入参。对比第三行,明显不一样,因为【切面修改了入参】
我们定义的修改规则是 id=id*id
第六行:接口打印返回值16。16=16+0
接口返回结果 :116 ,对比第六行,多了100,因为【切面中修改了 返回值】
我们定义的修改规则是 如果是Integer类型的返回值,则再加100返回
测试结果
- 成功获取方法的基本信息(类名、方法名、入参)
- 成功获取了注解
- 成功修改了入参
- 成功修改了返回值