最近開始學(xué)著做Java服務(wù)器端開發(fā),對(duì)注解一知半解的。今天剛好發(fā)現(xiàn)有一個(gè)業(yè)務(wù)場(chǎng)景可以通過自己實(shí)現(xiàn)注解簡化代碼,所以嘗試著做起來。我發(fā)現(xiàn)Annotation+AspectJ是一個(gè)非常強(qiáng)大的組合,可以做很多事情。
下面這個(gè)checkRamPermission方法是用來做權(quán)限檢查的?;旧细鱾€(gè)入口方法都需要在最開始的地方加上這行代碼??雌饋砗芊爆?,也不夠酷。所以琢磨用注解的方法來實(shí)現(xiàn)。
openApiUtil.checkRamPermission(request, request.getAction(), ResouceType.PRODUCT, String.valueOf(request.getProductId()), true);
checkRamPermission方法的參數(shù)分為好幾種。分別如下所示。
- 請(qǐng)求者傳入的參數(shù),如request。
- 不同語境下的固定參數(shù)。如ResouceType.PRODUCT。
- 不同語境下從request各子類里面提取的屬性。如request.getProductId()。
針對(duì)上面這些參數(shù)定義好注解。請(qǐng)求者傳入的參數(shù)可以通過反射獲取。注解里面只需包含語境相關(guān)的信息就行。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RAMAnnotation {
public String resourceType();
public String resourceId() default "";
}
接著定義好AspectJ的pointcut。
@Aspect
public class RAMPointCut {
@Pointcut("@annotation(com.alibaba.cloudmobile.mhub.util.RAMAnnotation)")
public void checkPemission() {}
}
接著就是實(shí)現(xiàn)攔截的方法。通過joinPoint獲取到方法的信息,包括參數(shù)值,這樣就可以獲取到request對(duì)象。因?yàn)閞equest子類眾多,需要resourceId輔助,以獲取具體子類方法,進(jìn)一步得到resourceId。
@Before(value = "com.alibaba.cloudmobile.mhub.util.RAMPointCut.checkPemission()")
public void ramCheckPemission(JoinPoint joinPoint) {
try {
MethodSignature methodSig = (MethodSignature) joinPoint.getSignature();
Annotation[] annotations = methodSig.getMethod().getDeclaredAnnotations();
RAMAnnotation annotation = (RAMAnnotation) annotations[0];
Object[] methodArgs = joinPoint.getArgs();
BasePopRequest request = (BasePopRequest) methodArgs[0];
String action = request.getAction();
String resourceId = null;
Method method = null;
if (annotation.resourceId().equals("productId")) {
method = request.getClass().getDeclaredMethod("getProductId");
} else if (annotation.resourceId().equals("appId")) {
method = request.getClass().getDeclaredMethod("getAppId");
}
if (method != null) {
resourceId = String.valueOf(method.invoke(request));
}
openApiUtil.checkRamPermission(request, action, annotation.resourceType(), resourceId, true);
} catch (Throwable e) {
logger.error("RAMAnnotation" + e.getMessage());
throw e;
}
}
使用方式如下。resourceType和resourceId使用枚舉類型會(huì)很合理一些。注解的屬性只支持Java的內(nèi)置類型,不支持用戶自定義的類型。
@RAMAnnotation(resourceType = "product", resourceId = "productId")
public QueryProductInfoResponse queryProductInfo(QueryProductInfoRequest request) throws Throwable
{
}
參考資料。