當JAVA注解、AOP舌镶、SpEL相遇柱彻,更多可能變?yōu)榱爽F(xiàn)實

image.png

常規(guī)情況下豪娜,我們可以通過業(yè)務定制化的注解,借助AOP機制來實現(xiàn)某些通用的處理策略哟楷。比如定義個@Permission注解侵歇,可以用于標識在具體的方法上,然后用來指定某個方法必須要指定角色的人才能夠訪問調用吓蘑。


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

這里惕虑,注解里面?zhèn)魅氲膮?shù)始終是編碼的時候就可以確定下來的固定值(role = UserRole.ADMIN)。

在業(yè)務開發(fā)中磨镶,也許你會遇到另一種場景:

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

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

這個時候账千,就輪到我們的主角“SpEL表達式”上場了侥蒙,借助EL表達式,可以讓我們將上面的想法變?yōu)楝F(xiàn)實匀奏。

下面講一下具體的做法鞭衩。

  1. 先定義一個業(yè)務注解,其中參數(shù)支持傳入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表達式聚磺,然后獲取方法的入?yún)⑴魈ǎx取EL表達式代表的真實的參數(shù)值,進而按照業(yè)務需要的邏輯進行處理瘫寝。

@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表達式的方式讀取對應參數(shù)值
         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進行業(yè)務自定義邏輯處理
    }
}

至此矢沿,通過EL表達式動態(tài)注解參數(shù)傳遞與解析處理的邏輯就都構建完成了滥搭。

  1. 具體業(yè)務使用的時候,直接通過EL表達式從請求體中動態(tài)的獲取到對應的參數(shù)值然后傳入到注解aspect切面處理邏輯中捣鲸,按照定制的業(yè)務邏輯進行統(tǒng)一處理瑟匆。

    @ResourceAccessPermission(objectId = "#reqBody.docUniqueId")
    public void deleteResource(DeleteResourceReqBody reqBody) {
        // do something here...
    }

借助JAVA注解 + AOP + SpEL的組合,會讓我們在很多實際問題的處理上變得游刃有余栽惶,可以抽象出很多公共通用的處理邏輯愁溜,實現(xiàn)通用邏輯與業(yè)務邏輯的解耦疾嗅,便于業(yè)務層代碼的開發(fā)。


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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末论悴,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子墓律,更是在濱河造成了極大的恐慌膀估,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件耻讽,死亡現(xiàn)場離奇詭異察纯,居然都是意外死亡,警方通過查閱死者的電腦和手機针肥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門饼记,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人慰枕,你說我怎么就攤上這事具则。” “怎么了捺僻?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵乡洼,是天一觀的道長。 經(jīng)常有香客問我匕坯,道長,這世上最難降的妖魔是什么拔稳? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任葛峻,我火速辦了婚禮,結果婚禮上巴比,老公的妹妹穿的比我還像新娘术奖。我一直安慰自己,他們只是感情好轻绞,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布采记。 她就那樣靜靜地躺著,像睡著了一般政勃。 火紅的嫁衣襯著肌膚如雪唧龄。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天奸远,我揣著相機與錄音既棺,去河邊找鬼讽挟。 笑死,一個胖子當著我的面吹牛丸冕,可吹牛的內容都是我干的耽梅。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼胖烛,長吁一口氣:“原來是場噩夢啊……” “哼眼姐!你這毒婦竟也來了?” 一聲冷哼從身側響起佩番,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤妥凳,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后答捕,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體逝钥,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年拱镐,在試婚紗的時候發(fā)現(xiàn)自己被綠了艘款。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡沃琅,死狀恐怖哗咆,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情益眉,我是刑警寧澤晌柬,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站郭脂,受9級特大地震影響年碘,放射性物質發(fā)生泄漏。R本人自食惡果不足惜展鸡,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一屿衅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧莹弊,春花似錦涤久、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至细疚,卻和暖如春蔗彤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工幕与, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留挑势,地道東北人。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓啦鸣,卻偏偏與公主長得像潮饱,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子诫给,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內容