Java 注解入门指南
1. 基本注解
常见的基本注解 :
- @Override : 重写方法的注解
- @Deprecated : 标识过期的注解
- @SfeVarargs : Java 7 中引入 , 抑制堆污染的注解
- @SuppressWarings(“unchecked”) : 抑制编译器警告
- @FunctionalIntrerface : 标识函数式接口 , 常用于Lambda表达式
2. 元注解
元注解 : 元注解修饰注解 , 而元注解也是一种注解 (套娃警告) , 它们是一种专门用于修饰非元注解的注解
上图可以看到有两个注解修饰了 @Override , 它们就是其中的两个元注解
JDK 5.0 提供了 4 个元注解 :
- @Retension
- @Target
- @Documented
- @Inherited
@Retension
@Retension 限定注解的生命周期
其中取值有 :
- SOURCE : 该注解只会保留在源文件中 , 被编译成 CLASS 文件就消失
- CLASS : 该注解会保留在 CLASS 文件中 , 在运行时 JVM 无法获取注解信息 , 是该注解的默认值
- RUNTIME : 该注解会跟随到运行时状态 , JVM 可以获取到该注解信息 , 程序也可以获取到注解信息作额外操作
示例 :
/**
* 自定义用户登录注解 , 运行时被该注解标识的方法会判断用户是否登录
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface UserLoginToken {
boolean required() default true;
}
由于 @UserLoginToken 被 RUNTIME
修饰 , 所以在运行时可以通过反射获取到该注解的属性值 , 作额外的逻辑处理
@Target
@Target 指定被修饰的注解能用于修饰哪些作用域
其中取值有 :
- ANNOTATION_TYPE :只能修饰注解
- CONSTRUCTOR : 只能修饰构造器
- FIELD : 只能修饰成员变量
- LOCAL_VARIABLE : 该策略的注解只能修饰局部变量
- METHOD : 该策略的注解只能修饰方法定义 (常用于自定义注解)
- PACKAGE : 只能修饰包定义
- TYPE : 该注解可以修饰 类 , 接口 (包括注解类型的接口) , 或者枚举定义
@Target 可以同时指定多个作用域 , @UserLoginToken 被标注只能作用再方法和类上 , 示例 :
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserLoginToken {
boolean required() default true;
}
@UserLoginToken 由于被 METHOD
和 TYPE
修饰 , 所以该注解可以修饰 方法 , 类 , 接口 等作用域
@Documented
@Documented 用于指定被该元注解修饰的注解将被 javadoc 工具提取成文档
示例 :
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface UserLoginToken {
boolean required() default true;
}
添加了 @Documented , 就会被 javadoc 工具抓取到文档中
@Inherited
被 @Inherited 注解修饰的注解将具备 继承性 , 即如果某个类使用了被 @Inherited 修饰的注解A , 那么子类也会具备该注解A
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface UserLoginToken {
boolean required() default true;
}
UML 类图结构 :
下面这段代码将会输出 true , 代表子类 Sub 上存在 @UserLoginToken注解
@UserLoginToken
public class Base {
}
public class Sub extends Base {
public static void main(String[] args) {
// true
System.out.println(Sub.class.isAnnotationPresent(UserLoginToken.class));
}
}
如果 @UserLoginToken 去掉 @Inherited 注解 , 程序会输出 false
3. 获取注解信息的方法
AnnotatedElement 接口是所有程序元素的父接口 (类 , 接口 , 方法 , 成员等) , 通过反射获取到该对象后 , 可以利用如下方法访问注解信息 :
- < A extends Annotation> A getAnnotation(Class annotationClass): 返回改程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回null。
- Annotation[] getAnnotations():返回该程序元素上存在的所有注解。
- boolean isAnnotationPresent(Class<?extends Annotation> annotationClass):判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false.
- Annotation[] getDeclaredAnnotations():返回直接存在于该程序元素的所有注解
- < A extends Annotation> getDeclaredAnnotation() : Java8 新增的方法 , 该方法尝试获取直接修饰该程序元素 , 指定类型的注解 , 如果该类型的注解不存在 , 则返回 null
4. 注解实战案例
通过自定义注解 @UserLoginToken 判断用户是否已登录
// 用户登录验证注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserLoginToken {
boolean required() default true;
}
// 允许匿名访问注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PassToken {
boolean required() default true;
}
拦截器拦截请求 :
public class MemberAuthenticationInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
// 如果不是映射到方法直接通过
if(!(object instanceof HandlerMethod)){
return true;
}
// 强转成对应方法
HandlerMethod handlerMethod=(HandlerMethod)object;
Method method=handlerMethod.getMethod();
//检查是否有@Passtoken注释,有则跳过认证
if (method.isAnnotationPresent(PassToken.class)) {
PassToken passToken = method.getAnnotation(PassToken.class);
if (passToken.required()) {
return true;
}
}
//检查有没有需要用户权限的注解
if (method.isAnnotationPresent(UserLoginToken.class)) {
// 获取该方法上的注解 , 并获取它的属性值
UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
if (userLoginToken.required()) {
// 执行认证
}
}
return true;
}
}
如果觉得不错 , 欢迎点赞收藏 ! 随时欢迎各位与我交流学习 !