<input id="ohw05"></input>
  • <table id="ohw05"><menu id="ohw05"></menu></table>
  • <var id="ohw05"></var>
  • <code id="ohw05"><cite id="ohw05"></cite></code>
    <label id="ohw05"></label>
    <var id="ohw05"></var>
  • 當JAVA注解、AOP、SpEL相遇,更多可能變為了現實

    常規情況下,我們可以通過業務定制化的注解,借助AOP機制來實現某些通用的處理策略。比如定義個@Permission注解,可以用于標識在具體的方法上,然后用來指定某個方法必須要指定角色的人才能夠訪問調用。

    
        // 標識只有管理員角色才能調用此接口
        @Permission(role = UserRole.ADMIN)
        public void deleteResource(DeleteResourceReqBody reqBody) {
            // do something here...
        }
    
    

    這里,注解里面傳入的參數始終是編碼的時候就可以確定下來的固定值(role = UserRole.ADMIN)。

    在業務開發中,也許你會遇到另一種場景:

    比如有個文檔資源控制接口,你需要判斷出當前用戶操作的目標文檔ID,然后去判斷這個用戶是否有此文檔的操作權限。

    我們希望能夠使用注解的方式來實現,需要能夠將動態的文檔ID通過注解傳遞,然后在Aspect處理類中獲取到文檔ID然后進行對應的權限控制。但是按照常規方式去寫代碼的時候,會發現并不支持直接傳遞一個請求對象到注解中。

    這個時候,就輪到我們的主角“SpEL表達式”上場了,借助EL表達式,可以讓我們將上面的想法變為現實。

    下面講一下具體的做法。

    1. 先定義一個業務注解,其中參數支持傳入EL表達式
    
    @Inherited
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD})
    public @interface ResourceAccessPermission {
        /**
         * 操作的目標資源的唯一ID, 支持EL表達式
         *
         * @return  ID
         */
        String objectId();
    }
    
    
    1. 編寫EL表達式的解析器,如下所示:
    
    public class ExpressionEvaluator<T> 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 T condition(String conditionExpression, AnnotatedElementKey elementKey, EvaluationContext evalContext, Class<T> clazz) {
            return getExpression(this.conditionCache, elementKey, conditionExpression).getValue(evalContext, clazz);
        }
    
        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);
                this.targetMethodCache.put(methodKey, targetMethod);
            }
            return targetMethod;
        }
    
    }
    
    @Getter
    @ToString
    @AllArgsConstructor
    public class ExpressionRootObject {
        private final Object object;
        private final Object[] args;
    }
    
    
    1. 編寫對應的Aspect切換處理類,借助上面的EL解析器進行獲取注解中的傳入的EL表達式,然后獲取方法的入參,讀取EL表達式代表的真實的參數值,進而按照業務需要的邏輯進行處理。
    
    @Component
    @Aspect
    @Slf4j
    public class ResourceAccessPermissionAspect {
        private ExpressionEvaluator<String> evaluator = new ExpressionEvaluator<>();
    
        @Pointcut("@annotation(com.vzn.demo.ResourceAccessPermission)")
        private void pointCut() {
    
        }
    
        @Before("pointCut()")
        public void doPermission(JoinPoint joinPoint) {
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            Method method = methodSignature.getMethod();
             ResourceAccessPermission permission = method.getAnnotation(ResourceAccessPermission.class);
            if (joinPoint.getArgs() == null) {
                return;
            }
    
            // [重點]EL表達式的方式讀取對應參數值
             EvaluationContext evaluationContext = evaluator.createEvaluationContext(joinPoint.getTarget(),
                    joinPoint.getTarget().getClass(), ((MethodSignature) joinPoint.getSignature()).getMethod(),
                    joinPoint.getArgs());
             AnnotatedElementKey methodKey =
                     new AnnotatedElementKey(((MethodSignature) joinPoint.getSignature()).getMethod(),
                            joinPoint.getTarget().getClass());
    
            // 讀取objectID,如果以#開頭則按照EL處理,否則按照普通字符串處理
            String objectId;
            if (StringUtils.startsWith(permission.objectId(), "#")) {
                objectId = evaluator.condition(permission.objectId(), methodKey, evaluationContext, String.class);
            } else {
                objectId = permission.objectId();
            }
    
            // TODO 對objectID進行業務自定義邏輯處理
        }
    }
    
    

    至此,通過EL表達式動態注解參數傳遞與解析處理的邏輯就都構建完成了。

    1. 具體業務使用的時候,直接通過EL表達式從請求體中動態的獲取到對應的參數值然后傳入到注解aspect切面處理邏輯中,按照定制的業務邏輯進行統一處理。
    
        @ResourceAccessPermission(objectId = "#reqBody.docUniqueId")
        public void deleteResource(DeleteResourceReqBody reqBody) {
            // do something here...
        }
    
    

    借助JAVA注解 + AOP + SpEL的組合,會讓我們在很多實際問題的處理上變得游刃有余,可以抽象出很多公共通用的處理邏輯,實現通用邏輯與業務邏輯的解耦,便于業務層代碼的開發。


    我是悟道君,聊技術、又不僅僅聊技術~
    期待與你一起探討,一起成長為更好的自己。

    posted @ 2022-06-20 09:09  架構悟道  閱讀(269)  評論(2編輯  收藏  舉報
    国产美女a做受大片观看