SPEL 表达式
Spring 提供的EL表达式解析特性强化了注解能力。能做到注解中的属性值动态地从方法参数中获取,而不是简单地设置固定值。
自定义注解
使用 Java 原生注解自定义注解。我们想要做到 source 属性从方法参数中动态获取。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HaxService {
/**数据来源*/
String source() default "default";
}
// 使用效果:
@HaxService(source = "#source.name()")
public List<ImportResp> synStock(OriStockDTO oriStockDTO, StockSourceEnum source) throws Exception {
...
}
解析SPEL表达式
/**
* 哈希Service切面
* 1.记录请求报文、响应报文
* 2.记录接口耗时
* @author 曲元涛
* @date 2020/11/30 15:33
*/
@Component
@Aspect
public class HaxServiceAspect {
private static final Logger logger = LoggerFactory.getLogger(HaxServiceAspect.class);
/**方法参数名解析器*/
private final DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();
/**Spel表达式解析器*/
private final SpelExpressionParser parser = new SpelExpressionParser();
/**SPEL表达式标识符*/
public final String SPEL_FLAG = "#";
@Resource
private ApiRecordService apiRecordService;
@Resource
private LinkThreadPoolExecutor linkThreadPoolExecutor;
@Pointcut("@annotation(com.hand.business.tool.annotation.HaxService)")
private void haxServiceAspect() {}
@Around(value = "haxServiceAspect()")
public Object haxServiceAspect(ProceedingJoinPoint joinPoint) throws Throwable {
// 请求时间
String requestTime = DateUtil.dateToStr(new Date(), DateUtil.DATE_TIME);
MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
Method method = methodSignature.getMethod();
final String methodName = method.getName();
// 方法形参名称
String[] argNames = HaxBeanTool.getOrDefault(nameDiscoverer.getParameterNames(method), new String[0]);
// 方法实参值
Object[] args = joinPoint.getArgs();
HaxService haxService = method.getAnnotation(HaxService.class);
// 数据来源
String dataSource = this.analysisSpel(haxService.source(), argNames, args);
// 切面方法形参值
JSONObject requestParam = new JSONObject();
for (int i = 0; i < argNames.length; i++) {
String argName = argNames[i];
Object argVal = args[i];
requestParam.put(argName, argVal);
}
StopWatch stopWatch = new StopWatch();
stopWatch.start(methodName);
try {
Object result = joinPoint.proceed();
stopWatch.stop();
long rt = stopWatch.getTotalTimeMillis();
ApiRecord sucRecord = new ApiRecord(dataSource, methodName, requestParam, result, rt);
sucRecord.setRequestTime(requestTime);
sucRecord.setSuccess(ProcessRet.success.name());
this.doLog(sucRecord);
return result;
} catch (Throwable throwable) {
logger.info("[haxServiceAspect] method [{}] proceed throw exception... [{}]",
methodName, throwable.getMessage());
stopWatch.stop();
long rt = stopWatch.getTotalTimeMillis();
ApiRecord failRecord =
new ApiRecord(dataSource, methodName, requestParam, throwable.getMessage(), rt);
failRecord.setRequestTime(requestTime);
failRecord.setSuccess(ProcessRet.failed.name());
failRecord.setErrorMsg(throwable.getMessage());
this.doLog(failRecord);
throw throwable;
}
}
private void doLog(ApiRecord record) {
linkThreadPoolExecutor.execute(() -> {
if (StringUtils.isBlank(record.getRequestTime())) {
record.setRequestTime(DateUtil.dateToStr(new Date(), DateUtil.DATE_TIME));
}
try {
apiRecordService.insert(record);
} catch (Exception e) {
logger.info("保存接口日志异常[{}]", e.getMessage());
}
});
}
/**
* 解析SPEL表达式
*
* @author 曲元涛
* @date 2020/11/30 下午6:05
* @param spel SPEL表达式
* @param argNames 形参名称数组
* @param args 实参数组
*/
private String analysisSpel(String spel, String[] argNames, Object[] args) {
if (!StrUtil.contains(spel, SPEL_FLAG)) {
return spel;
}
// 解析后的SPEL
Expression expression = parser.parseExpression(spel);
// spring表达式上下文
EvaluationContext context = new StandardEvaluationContext();
// 给上下文赋值变量
for (int i = 0; i < argNames.length; i++) {
context.setVariable(argNames[i], args[i]);
}
return HaxBeanTool.getOrDefault(expression.getValue(context), "").toString();
}
}