自定义注解支持Spring EL

雪域幽狐 2018-12-21 13:49 阅读:9114


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);
    }
}

0条评论

登陆后可评论