JAVA注解那些事

Annotation(注解)
是 Java 提供的一種對(duì)元程序中元素關(guān)聯(lián)信息和元數(shù)據(jù)(metadata)的途徑和方法。
Annatation(注解)是一個(gè)接口,程序可以通過(guò)
反射
來(lái)獲取指定程序中元素的 Annotation對(duì)象,然后通過(guò)該Annotation 對(duì)象來(lái)獲取注解中的元數(shù)據(jù)信息。
元注解
的作用是負(fù)責(zé)注解其他注解。Java5.0 定義了 4 個(gè)標(biāo)準(zhǔn)的 meta-annotation 類型,它們被用來(lái)提供對(duì)其它 annotation 類型作說(shuō)明。
1、@Target 修飾的對(duì)象范圍
@Target說(shuō)明了Annotation所修飾的對(duì)象范圍:Annotation可被用于 packages、types(類、接口、枚舉、Annotation 類型)、類型成員(方法、構(gòu)造方法、成員變量、枚舉值)、方法參數(shù)和本地變量(如循環(huán)變量、catch 參數(shù))。在 Annotation 類型的聲明中使用了 target 可更加明晰其修飾的目標(biāo)
2、@Retention 定義被保留的時(shí)間長(zhǎng)短
Retention 定義了該 Annotation 被保留的時(shí)間長(zhǎng)短:表示需要在什么級(jí)別保存注解信息,用于描述注解的生命周期(即:被描述的注解在什么范圍內(nèi)有效),取值(RetentionPoicy)由:
-
SOURCE:在源文件中有效(即源文件保留)
-
CLASS:在 class 文件中有效(即 class 保留)
-
RUNTIME:在運(yùn)行時(shí)有效(即運(yùn)行時(shí)保留)
3、@Documented 描述-javadoc
@ Documented 用于描述其它類型的 annotation 應(yīng)該被作為被標(biāo)注的程序成員的公共 API,因此可以被例如 javadoc 此類的工具文檔化。
4、@Inherited 闡述了某個(gè)被標(biāo)注的類型是被繼承的
@Inherited 元注解是一個(gè)標(biāo)記注解,@Inherited 闡述了某個(gè)被標(biāo)注的類型是被繼承的。如果一個(gè)使用了@Inherited 修飾的 annotation 類型被用于一個(gè) class,則這個(gè) annotation 將被用于該class 的子類。

Java 的基本注解和元注解,如果這兩種注解不能滿足你的需求,可以自定義注解。Java SE5 擴(kuò)展了反射機(jī)制的 API,以幫助程序員快速的構(gòu)造自定義注解處理器。默認(rèn)情況下,注解可以在程序的任何地方使用,通常用于修飾類、接口、方法和變量等。
下面實(shí)現(xiàn)一個(gè)注解處理器(自定義注解)。
1、定義注解
使用
@interface
關(guān)鍵字聲明自定義注解:
不包含任何成員變量的注解稱為
標(biāo)記注解
,基本注解中的 @Override 注解都屬于標(biāo)記注解。
// 定義一個(gè)簡(jiǎn)單的注解類型
public @interface Test {
}
根據(jù)需要,注解中可以定義成員變量,成員變量以無(wú)形參的方法形式來(lái)聲明,成員變量也可以有訪問(wèn)權(quán)限修飾符,但是只能有公有權(quán)限和默認(rèn)權(quán)限。
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface UvLogAnnotation {
// 定義帶成員變量的注解。注解中的成員變量也可以有默認(rèn)值,可使用 default 關(guān)鍵字。
// 注解中的成員變量以方法的形式來(lái)定義,其方法名和返回值定義了該成員變量的名字和類型
String methodKey() default "";
}
根據(jù)注解是否包含成員變量,可以分為如下兩類:
1、標(biāo)記注解:沒(méi)有定義成員變量的注解類型被稱為標(biāo)記注解。這種注解僅利用自身的存在與否來(lái)提供信息,如前面介紹的 @Override、@Test 等都是標(biāo)記注解。
2、元數(shù)據(jù)注解:包含成員變量的注解,因?yàn)樗鼈兛梢越邮芨嗟脑獢?shù)據(jù),所以也被稱為元數(shù)據(jù)注解。
2、Aspect切面類處理
/**
* 瀏覽量統(tǒng)計(jì)
*/
@Aspect
@Component
@Slf4j
public class UvLogAspect {
@Autowired
private RedisService redisService;
@Autowired
private UserLogService userLogService;
// 拿到@UVlog注解注釋的方法,這里就是切點(diǎn)
@Pointcut("@annotation(com.aop.annotation.UvLogAnnotation)")
private void serviceAspect() {
}
// 調(diào)用方法后都會(huì)進(jìn)行統(tǒng)計(jì)操作,寫入redis
@After("serviceAspect()")
public void afterMethod(JoinPoint joinPoint) {
log.info("開(kāi)始統(tǒng)計(jì)處理:**************");
MethodSignature sign = (MethodSignature) joinPoint.getSignature();
Method method = sign.getMethod();
// 獲取指定注解實(shí)例
UvLogAnnotation annotation = method.getAnnotation(UvLogAnnotation.class);
String methodKey = annotation.methodKey();
String[] argNames = sign.getParameterNames();
List<String> argNameList = Arrays.asList(argNames);
Map<String, Object> paramsMap = new HashMap<>();
//處理參數(shù)(將參數(shù)或者對(duì)象屬性值統(tǒng)一放到paramsMap中)
resolveArgs(joinPoint, argNameList, paramsMap);
log.info("開(kāi)始記錄redis:{}", JSONObject.toJSONString(paramsMap));
try {
saveToRedis(methodKey, paramsMap);
} catch (Exception e) {
log.info("redis 異常,降級(jí)處理!");
}
}
public void saveToRedis(String methodKey, Map<String, Object> paramsMap) {
if (UvLogKeyEnum.DCZS_COUNT.getValue().equals(methodKey)) {
userLogService.saveToRedis(methodKey, paramsMap);
}
}
public static void resolveArgs(JoinPoint joinPoint, List<String> argNameList,
Map<String, Object> paramsMap) {
Object[] argObjects = joinPoint.getArgs(); // 參數(shù)對(duì)象集合
for (int i = 0; i < argNameList.size(); i++) {
Class<?> classo = argObjects[i].getClass();
if (handleClassType(classo.getName())) {
paramsMap.put(argNameList.get(i), joinPoint.getArgs()[i]);
continue;
}
JSONObject object = (JSONObject) JSONObject.toJSON(joinPoint.getArgs()[i]);
object.keySet().parallelStream().forEach(p -> {
if (!p.equals("grantedAuthorities")) {
paramsMap.put(p, object.get(p));
}
});
}
}
public static <T> T get(Class<T> clz, Object o) {
if (clz.isInstance(o)) {
return clz.cast(o);
}
return null;
}
public static boolean handleClassType(String name) {
AtomicBoolean result = new AtomicBoolean(false);
String[] className = {"String", "Integer", "Long", "int", "float", "double", "char"};
Arrays.asList(className).forEach(n -> {
if (name.contains(n)) {
result.set(name.contains(n));
}
});
return result.get();
}
3、注解的使用
Controller中使用自定義注解@UvLogAnnotation:
@RestController
@RequestMapping("/api")
@Slf4j
@Api(tags = "統(tǒng)計(jì)用戶流量次數(shù)")
public class UserController {
@Autowired
private UserInfoService userInfoService;
@Autowired
private RedisService redisService;
@ApiOperation(value = "統(tǒng)計(jì)用戶流量次數(shù)")
@GetMapping(value = "/user/count")
// 使用帶成員變量的注解時(shí),需要為成員變量賦值
@UvLogAnnotation(methodKey = "USER_COUNT")
public Result<Integer> countUserViews(@ApiIgnore OauthUser user) {
if (user == null) {
return Result.error(BasicCodeMsg.AUTHORIZATION_ERROR);
}
user.setGmtCreate(new Date());
Integer result = userInfoService.countUserViews(user);
return Result.success(result);
}

