Spring EL功能强大,使用时也很方便,比如在使用以下代码时,如果传入的productId为123456,imgType的id为1,那Spring EL可解析为123456_1。
@Cacheable(value = "productImg", key = "#productId+ '_' +#imgType.id")
public List<String> listImg(long productId, ProductImgType imgType) {
return new ArrayList<String>();
}
我们在自定义注解时,也期望能达到相同效果,那怎么办呢。分析Spring源码后,复用其代码即可。
首先,我们自定义一个注解。
package com.nowfox.commons.annotation;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Documented
@Inherited
@Retention(RUNTIME)
@Target({ METHOD })
public @interface CacheRemove {
String value();
String keyName() default "";
String key() default "";
}
其次引入以下3个解析文件。
package com.nowfox.commons.el;
import java.lang.reflect.Method;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.cache.interceptor.SimpleKeyGenerator;
import org.springframework.context.expression.AnnotatedElementKey;
import org.springframework.expression.EvaluationContext;
import org.springframework.util.StringUtils;
public class AspectSupportUtils {
private static ExpressionEvaluator evaluator = new ExpressionEvaluator();
public static Object getKeyValue(JoinPoint joinPoint, String keyExpression) {
return getKeyValue(joinPoint.getTarget(), joinPoint.getArgs(), joinPoint.getTarget().getClass(),
((MethodSignature) joinPoint.getSignature()).getMethod(), keyExpression);
}
private static Object getKeyValue(Object object, Object[] args, Class<?> clazz, Method method,
String keyExpression) {
if (StringUtils.hasText(keyExpression)) {
EvaluationContext evaluationContext = evaluator.createEvaluationContext(object, clazz, method, args);
AnnotatedElementKey methodKey = new AnnotatedElementKey(method, clazz);
return evaluator.key(keyExpression, methodKey, evaluationContext);
}
return SimpleKeyGenerator.generateKey(args);
}
}
核心主要是这个,引入Spring的解析
package com.nowfox.commons.el;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.aop.support.AopUtils;
import org.springframework.context.expression.AnnotatedElementKey;
import org.springframework.context.expression.CachedExpressionEvaluator;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
public class ExpressionEvaluator extends CachedExpressionEvaluator {
private final ParameterNameDiscoverer paramNameDiscoverer = new DefaultParameterNameDiscoverer();
private final Map<ExpressionKey, Expression> conditionCache = new ConcurrentHashMap<>(64);
private final Map<AnnotatedElementKey, Method> targetMethodCache = new ConcurrentHashMap<>(64);
public EvaluationContext createEvaluationContext(Object object, Class<?> targetClass, Method method,
Object[] args) {
Method targetMethod = getTargetMethod(targetClass, method);
ExpressionRootObject root = new ExpressionRootObject(object, args);
return new MethodBasedEvaluationContext(root, targetMethod, args, this.paramNameDiscoverer);
}
public Object key(String conditionExpression, AnnotatedElementKey elementKey, EvaluationContext evalContext) {
return getExpression(this.conditionCache, elementKey, conditionExpression).getValue(evalContext);
}
private Method getTargetMethod(Class<?> targetClass, Method method) {
AnnotatedElementKey methodKey = new AnnotatedElementKey(method, targetClass);
Method targetMethod = this.targetMethodCache.get(methodKey);
if (targetMethod == null) {
targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
if (targetMethod == null) {
targetMethod = method;
}
this.targetMethodCache.put(methodKey, targetMethod);
}
return targetMethod;
}
}
package com.nowfox.commons.el;
public class ExpressionRootObject {
private final Object object;
private final Object[] args;
public ExpressionRootObject(Object object, Object[] args) {
this.object = object;
this.args = args;
}
public Object getObject() {
return object;
}
public Object[] getArgs() {
return args;
}
}
然后,配置AOP XML,当然也可以使用注解方式配置。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:config>
<aop:aspect id="cache" ref="cacheServiceImpl">
<aop:pointcut id="cacheRemove" expression="@annotation(cacheRemove)" />
<aop:after-returning method="remove" pointcut-ref="cacheRemove" />
</aop:aspect>
</aop:config>
</beans>
最后,是自定义注解解析类。
package com.nowfox.www.service.impl;
import org.aspectj.lang.JoinPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import com.nowfox.commons.annotation.CacheRemove;
import com.nowfox.commons.el.AspectSupportUtils;
@Service
public class CacheServiceImpl implements CacheService{
private final Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 本方法用于处理缓存删除注解
* {@link com.nowfox.commons.annotation.CacheRemove}
*
* @param jp
* @return
*/
public void remove(JoinPoint jp, CacheRemove cacheRemove) {
Object keyValue = AspectSupportUtils.getKeyValue(jp, cacheRemove.key());
logger.debug("keyValue={}",keyValue);
}
}