自定义注解:有入参的注解&实现打印接口入参
前言
写此文章的目的:
1:工作上遇到使用自定义注解进行分布式锁,注解可以传入参数。(必须记录这种高级用法)
2:重写下AOP实现打印接口入参(以前的写的不好)
一、pom依赖
整个项目就只有这么点依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.79</version>
<scope>compile</scope>
</dependency>
</dependencies>
二、自定义注解(有入参)
示例:
方法上的注解:
@ParamAnnotation(lockKey = "#userRequest.name,#userRequest.jobRequest.jobName",expireTime = 1500)
入参:
{
"name":"lt-test paramAnnotation",
"jobRequest":{
"jobId":"123456789",
"jobName":"oooooo"
}
}
解析获取到的值是一个List<String>:
["lt-test paramAnnotation","oooooo"]
1.定义注解
代码如下(示例):
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ParamAnnotation {
/**
* 分布式锁 key
*/
String lockKey();
/**
* 分布式锁 value
*/
String lockValue() default "lockValue";
/**
* 分布式锁 过期时间
*/
int expireTime() default 300;
}
2.注解的Aspect
import com.tao.demo.annotation.ParamAnnotation;
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.reflect.MethodSignature;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.annotation.Order;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@Aspect
@Component
@Slf4j
@Order(2)
public class ParamAnnotationAspect {
private static LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
/**
* Around 环绕增强通知
*
* @param joinPoint 连接点,所有方法都属于连接点;但是当某些方法上使用了@PrintLog自定义注解时,
* 则其将连接点变为了切点;然后在切点上织入额外的增强处理;切点和其相应的增强处理构成了切面Aspect 。
*/
@Around("@annotation(com.tao.demo.annotation.ParamAnnotation)")
public Boolean handlerPrintLog(ProceedingJoinPoint joinPoint) {
ParamAnnotation paramAnnotation = ((MethodSignature) joinPoint.getSignature())
.getMethod().getAnnotation(ParamAnnotation.class);
// 获取自定义注解对象中的属性值
String lockKey = paramAnnotation.lockKey();
String lockValue = paramAnnotation.lockValue();
int expireTime = paramAnnotation.expireTime();
log.info("lockKey = {}", getLockKey(lockKey));
log.info("lockValue = {}", lockValue);
log.info("expireTime = {}", expireTime);
Object target = joinPoint.getTarget();
Method method = this.joinPointMethod(joinPoint);
Object[] param = joinPoint.getArgs();
List<String> str = parseKeyELs(lockKey,target,method, param);
log.info("获取注解入参里的lockKey对应的值:{}",str);
//可以在这里调用redisService获取分布式锁
// if (redisService.tryGetDistributedLock(lockKey, LockValue, expireTime)) {
try {
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
} finally {
//释放分布式锁
log.info("释放分布式锁");
}
// }
return false;
}
private List<String> parseKeyELs(String keyEL, Object target, Method method, Object[] args) {
if (StringUtils.isEmpty(keyEL)) {
return Collections.emptyList();
} else {
List<String> keyELs = Arrays.asList(keyEL.split(","));
return (List) keyELs.stream().map((spVal) -> {
return parseEL(spVal, target, method, args);
}).collect(Collectors.toList());
}
}
private Method joinPointMethod(ProceedingJoinPoint point) {
return ((MethodSignature)point.getSignature()).getMethod();
}
public static String parseEL(String spEL, Object root, Method method, Object[] args) {
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new MethodBasedEvaluationContext(root, method, args, discoverer);
if (null != args && args.length > 0) {
String[] names = discoverer.getParameterNames(method);
for (int i = 0; i < names.length; ++i) {
context.setVariable(names[i], args[i]);
}
}
return (String) parser.parseExpression(spEL).getValue(context, String.class);
}
private String getLockKey(String lockKey) {
StringBuilder stringBuilder = new StringBuilder("redisLockKey");
List<String> keyList = Arrays.asList(lockKey.split(","));
keyList.forEach(key -> {
stringBuilder.append(":").append(key);
});
return stringBuilder.toString();
}
}
二. 打印入参
1.定义注解
代码如下(示例):
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PrintLog {
}
2.注解的Aspect
@Aspect
@Component
@Slf4j
@Order(1)
public class PrintLogAspect {
/**
* Around 环绕增强通知
*
* @param joinPoint 连接点,所有方法都属于连接点;但是当某些方法上使用了@PrintLog自定义注解时,
* 则其将连接点变为了切点;然后在切点上织入额外的增强处理;切点和其相应的增强处理构成了切面Aspect 。
*/
@Around("@annotation(com.tao.demo.annotation.PrintLog)")
public Object handlerPrintLog(ProceedingJoinPoint joinPoint) {
// 获取方法的名称
String methodName = joinPoint.getSignature().getName();
// 获取方法入参
Object[] param = joinPoint.getArgs();
StringBuilder sb = new StringBuilder();
for (Object o : param) {
sb.append(o).append("; ");
}
log.info("-----");
log.info("进入《{}》方法, 参数为: ", methodName);
log.info(JSONObject.toJSONString(sb));
log.info("-----");
Object object = null;
// 继续执行方法
try {
object = joinPoint.proceed();
} catch (Throwable throwable) {
log.error("打印日志处理error。。", throwable);
}
log.info("{} 方法执行结束。。", methodName);
return object;
}
}
三. controller
/**
* @author lobster.long
* @date 2022/6/14
*/
@Slf4j
@RestController()
@RequestMapping("demo")
public class DemoController {
@PrintLog
@PostMapping(value = "test")
public void test(@RequestBody UserRequest userRequest){
log.info(JSONObject.toJSONString(userRequest));
}
@ParamAnnotation(lockKey = "#userRequest.name,#userRequest.jobRequest.jobName",expireTime = 1500)
@PostMapping(value = "paramAnnotation")
public void paramAnnotation(@RequestBody UserRequest userRequest){
log.info(JSONObject.toJSONString(userRequest));
}
}
四. request
请求对象:UserRequest
@Data
public class UserRequest {
private String name;
private JobRequest jobRequest;
}
请求对象:JobRequest
@Data
public class JobRequest {
private String jobId;
private String jobName;
}
执行结果
1.@ParamAnnotation结果
2.@PrintLog结果
总结
支持入参的注解,感觉很高级的样子 ~ ~
陌生人,加油,一起学习进步。