一、概念
Annotation(注解)是 Java 提供的一种对元程序中元素关联信息和元数据(metadata)的途径和方法。
Annatation(注解)是一个接口,程序可以通过反射
来获取指定程序中元素的 Annotation对象,然后通过该Annotation 对象来获取注解中的元数据信息。
二、 5 种元注解
1. @Retention注解指定了标记的注解如何存储:
RetentionPolicy.SOURCE,标记的注解仅保留在源代码级别,并被编译器忽略。
RetentionPolicy.CLASS,标记的注解在编译时由编译器保留,但被Java虚拟机(JVM)忽略。
RetentionPolicy.RUNTIME,标记的注解由JVM保留,以便运行时环境可以使用。
2. @Documented注解指示每当使用指定的注解时,应使用Javadoc工具记录这些元素。
3. @Target注解标记了另一个注解,以限制该注解可以应用于哪种Java元素。@Target注解指定以下元素类型之一作为其值:
ElementType.ANNOTATION_TYPE,可以应用于注解类型。
ElementType.CONSTRUCTOR,可以应用于构造函数。
ElementType.FIELD,可以应用于字段或属性。
ElementType.LOCAL_VARIABLE,可以应用于局部变量。
ElementType.METHOD,可以应用于方法级注解。
ElementType.PACKAGE,可以应用于包声明。
ElementType.PARAMETER,可以应用于方法的参数。
ElementType.TYPE,可以应用于类的任何元素。
4. @Inherited注解表示该注解类型可以从超类继承(默认情况下并非如此)。当用户查询注解类型并且该类没有该类型的注解时,将查询该类的超类以获取注解类型。此注解仅适用于类声明。
5. @Repeatable注解是JavaSE8中引入的,表示标记的注解可以多次应用于同一声明或类型使用。
三、(自定义注解)注解处理器
Java 的基本注解和元注解,如果这两种注解不能满足你的需求,可以自定义注解。Java SE5 扩展了反射机制的 API,以帮助程序员快速的构造自定义注解处理器。默认情况下,注解可以在程序的任何地方使用,通常用于修饰类、接口、方法和变量等。
下面实现一个注解处理器(自定义注解)。
1、定义注解
使用 @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、元数据注解:包含成员变量的注解,因为它们可以接受更多的元数据,所以也被称为元数据注解。
2、Aspect切面类处理
@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 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 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§);
}
});
}
}
public static T get(Class 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 = “统计用户流量次数”)
public class UserController {
@Autowired
private UserInfoService userInfoService;
@Autowired
private RedisService redisService;
@ApiOperation(value = “统计用户流量次数”)
@GetMapping(value = “/user/count”)
// 使用带成员变量的注解时,需要为成员变量赋值
@UvLogAnnotation(methodKey = “USER_COUNT”)
public Result 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);
}