写在前面
在Java开发中,自定义注解为代码提供了丰富的元数据支持,而Spring表达式语言(SpEL)则允许在运行时动态查询和操作对象。将这两者结合,可以极大地增强代码的动态性和灵活性。
核心代码实现
/**
* 用于SpEL表达式解析.
*/
private SpelExpressionParser parser = new SpelExpressionParser();
/**
* 用于获取方法参数定义名字.
*/
private DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
Method method = methodSignature.getMethod();
if(method.isAnnotationPresent(DynamicTable.class)){
DynamicTable dynamicTable = (DynamicTable) method.getAnnotation(DynamicTable.class);
String key = dynamicTable.separateKey();
// 获取参数
Object[] args = joinPoint.getArgs();
Expression expression = parser.parseExpression(key);
// 使用spring的DefaultParameterNameDiscoverer获取方法形参名数组
String[] paramNames = nameDiscoverer.getParameterNames(method);
// spring的表达式上下文对象
EvaluationContext context = new StandardEvaluationContext();
// 给上下文赋值
for (int i = 0; i < args.length; i++) {
context.setVariable(paramNames[i], args[i]);
}
RequestDataHelper.setRequestData(new HashMap<String, Object>() {{
put(TABLE_NAME, dynamicTable.tableName());
put(TABLE_SUFFIX, expression.getValue(context));
}});
Object proceed = joinPoint.proceed();
RequestDataHelper.removeRequestData();
return proceed;
}else{
Object proceed = joinPoint.proceed();
return proceed;
}
代码说明:
SpelExpressionParser用于解析SpEL表达式。StandardEvaluationContext用于创建表达式的上下文环境。LocalVariableTableParameterNameDiscoverer用于获取方法参数的名字。 实现步骤:
- 解析表达式:
parser.parseExpression(key) - 表达式上下文环境设置值:
// spring的表达式上下文对象
EvaluationContext context = new StandardEvaluationContext();
// 给上下文赋值
for (int i = 0; i < args.length; i++) {
context.setVariable(paramNames[i], args[i]);
}
3.计算表达式结果:expression.getValue(context)
备注:上述代码是我在实现分表功能用的一个注解切面实现
在注解时使用SpEL表达式
- 引用方法入参:
#参数名 - 引用方法的pojo类型的属性参数:
#pojo.property - 调用静态方法:
T(com.xx.xxx.xxxx).functionName(参数1,参数2.....). 其中括号里填“被调用的方法所属类的包路径”,可不是当前使用这个注解的类的包路径,并且调用的方法必须是静态方法,否则会报“找不带该方法”异常. - 调用非静态方法:
new com.xx.xxx.xxxx().functionName(参数1,参数2.....)`. 调用非静态方法,语法非常像new一个对象的写法,但不同的是不能直接“new 类名”,必须是“new 类的完整包路径+类名”,即使是调用的方法就在当前类中,也要严格按照规定格式写,否则一定报错。 - 引用枚举:
`T(com.xx.xxx.xxxx).DEMO.getCode(). 和调用静态方法类似