Annotation(注解)是 Java 提供的一种对元程序中元素关联信息和元数据(metadata)的途径和方法。
Annatation(注解)是一个接口,程序可以通过反射
来获取指定程序中元素的 Annotation对象,然后通过该Annotation 对象来获取注解中的元数据信息。
元注解
的作用是负责注解其他注解。 Java5.0 定义了 4 个标准的 meta-annotation 类型,它们被用来提供对其它 annotation 类型作说明。
@Target说明了Annotation所修饰的对象范围: Annotation可被用于 packages、types(类、接口、枚举、Annotation 类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch 参数)。在 Annotation 类型的声明中使用了 target 可更加明晰其修饰的目标
Retention 定义了该 Annotation 被保留的时间长短:表示需要在什么级别保存注解信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效),取值(RetentionPoicy)由:
@ Documented 用于描述其它类型的 annotation 应该被作为被标注的程序成员的公共 API,因此可以被例如 javadoc 此类的工具文档化。
@Inherited 元注解是一个标记注解,@Inherited 阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited 修饰的 annotation 类型被用于一个 class,则这个 annotation 将被用于该class 的子类。
Java 的基本注解和元注解,如果这两种注解不能满足你的需求,可以自定义注解。Java SE5 扩展了反射机制的 API,以帮助程序员快速的构造自定义注解处理器。默认情况下,注解可以在程序的任何地方使用,通常用于修饰类、接口、方法和变量等。
下面实现一个注解处理器(自定义注解)。
使用 @interface 关键字声明自定义注解:
不包含任何成员变量的注解称为标记注解
,基本注解中的 @Override 注解都属于标记注解。
// 定义一个简单的注解类型
public @interface Test {
}
根据需要,注解中可以定义成员变量,成员变量以无形参的方法形式来声明,成员变量也可以有访问权限修饰符,但是只能有公有权限和默认权限。
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface UvLogAnnotation {
// 定义带成员变量的注解。注解中的成员变量也可以有默认值,可使用 default 关键字。
// 注解中的成员变量以方法的形式来定义,其方法名和返回值定义了该成员变量的名字和类型
String methodKey() default "";
}
根据注解是否包含成员变量,可以分为如下两类:
1、标记注解:没有定义成员变量的注解类型被称为标记注解。这种注解仅利用自身的存在与否来提供信息,如前面介绍的 @Override、@Test 等都是标记注解。
2、元数据注解:包含成员变量的注解,因为它们可以接受更多的元数据,所以也被称为元数据注解。
/**
* 浏览量统计
*/
@Aspect
@Component
@Slf4j
public class UvLogAspect {
@Autowired
private RedisService redisService;
@Autowired
private UserLogService userLogService;
// 拿到@UVlog注解注释的方法,这里就是切点
@Pointcut("@annotation(com.aop.annotation.UvLogAnnotation)")
private void serviceAspect() {
}
// 调用方法后都会进行统计操作,写入redis
@After("serviceAspect()")
public void afterMethod(JoinPoint joinPoint) {
log.info("开始统计处理:**************");
MethodSignature sign = (MethodSignature) joinPoint.getSignature();
Method method = sign.getMethod();
// 获取指定注解实例
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<>();
//处理参数(将参数或者对象属性值统一放到paramsMap中)
resolveArgs(joinPoint, argNameList, paramsMap);
log.info("开始记录redis:{}", JSONObject.toJSONString(paramsMap));
try {
saveToRedis(methodKey, paramsMap);
} catch (Exception e) {
log.info("redis 异常,降级处理!");
}
}
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(); // 参数对象集合
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();
}
Controller中使用自定义注解@UvLogAnnotation:
@RestController
@RequestMapping("/api")
@Slf4j
@Api(tags = "统计用户流量次数")
public class UserController {
@Autowired
private UserInfoService userInfoService;
@Autowired
private RedisService redisService;
@ApiOperation(value = "统计用户流量次数")
@GetMapping(value = "/user/count")
// 使用带成员变量的注解时,需要为成员变量赋值
@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);
}