想在IDEA中支持方法提示, 需要下载SpEL Assistant插件, 用法在这里
这里仅说AOP时支持方法参数
DemoAop.java:
java
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DemoAop {
/**
* 支持SpEL表达式
*/
String value();
}
MyXXXAspect.java:
java
@EnableAspectJAutoProxy
@Aspect
@Slf4j
public class CustomMetadataAspect implements BeanFactoryAware {
private final ExpressionParser parser = new SpelExpressionParser();
private final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
@Setter
private BeanFactory beanFactory;
@Before("@annotation(demoAop)")
public void atBefore(JoinPoint joinPoint, DemoAop demoAop) {
String result = demoAop.value();
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method targetMethod = signature.getMethod();
Object[] args = joinPoint.getArgs();
// noinspection DataFlowIssue
MethodBasedEvaluationContext context =
new MethodBasedEvaluationContext(null, targetMethod, args, pnd);
context.setBeanResolver(new BeanFactoryResolver(beanFactory));
// 可添加额外加参数, 如after切点添加result返回值: context.setVariable("额外参数", 参数值);
result = parser.parseExpression(result).getValue(context, String.class);
log.info("result is: {}", result);
}
其实需要的代码行数特别少, 不需要花里胡哨的写法;
另外我一般会写上rootObject
(MethodBasedEvaluationContext
的第一个参数), 方便调用一些内容, 比如SpringSecurity
中的@PreAuthorize
中的hasRole()
/hasAnyRole()
/hasPermission()
等等方法, 都是通过rootObject
来实现的
示例我的一个RootObject:
java
@Data
@AllArgsConstructor
@SuppressWarnings({"unused"})
public class RobinMetadataRootObject {
private final Method method;
private final Object[] args;
private final Object target;
private final Class<?> targetClass;
public String getMethodName() {
return this.method.getName();
}
}
java
// 解析时
public void atBefore(JoinPoint joinPoint, DemoAop demoAop) {
...
// 初始化
RobinMetadataRootObject rootObject = new RobinMetadataRootObject(targetMethod, extractArgs(targetMethod, args),
joinPoint.getTarget(), joinPoint.getTarget().getClass());
// 赋值
MethodBasedEvaluationContext context =
new MethodBasedEvaluationContext(rootObject, targetMethod, args, parameterNameDiscoverer);
...
}
可以方便的取方法和函数名称
Spring
中的一些其他rootObject
, 可供参考
Cache: CacheExpressionRootObject
Security: SecurityExpressionRoot
一些常见SpEL用法, 这里仅做示例, rootObject以, 不做详细解释:
java
public String test(String id, Integer[] idList) {
return id;
}
"#id"
-- 直接取值"1 > 2 && 3 == 4 && 5 > 6"
关系运算符, 支持>
/<
/>=
/<=
/!=
, 其中null
小于任何值- 运算符支持纯字母等效替代:
lt
('<')、gt
('>')、le
('<=')、ge
('>=')、eq
('==')、ne
('!= ')、div
('/')、mod
('%')、not
('!') 不区分大小写 "#id.startsWith('a')"
-- 调用一些方法"#id?.startsWith('b')"
-- 空安全"getMethodName() + getMethod() + getArgs()[0] + #args[0]"
-- root上的字段/方法"#p0+#a0"
-- 以p
或a
前缀 + 参数序号来取值"@taskExecutor.activeCount"
-- 使用@
符号 + bean名称调用其他bean"1 + 2 - 3 * 4 / 5 - -6 % 7"
-- 加减乘除, 正负数, 取余"'abc' + true + null + 123"
-- 各种字面量"{1, 2, 3, 4, 5}.?[ #this > 3 ]"
-- 内联列表以及过滤(返回值为[4,5])"{1, 2, 3, 4, 5}.^[ #this > 1 ]"
-- 查找第一个符合条件的值(结果为2)"{1, 2, 3, 4, 5}.&[ #this > 1 ]"
-- 查找最后一个符合条件的值(结果为5)"#username ?: 'abcd'"
-- 默认值 如果username
为 null, 则赋值为"abcd""new String(234)"
-- 创建对象- "true ? 1 : 2" -- 三元表达式