5分钟搞懂java注解@Annotation的具体使用

首先一句话结论:注解就是一种通过在类、方法、或者属性等上使用类似@xxx的方式进行“打标签”,然后可以通过反射机制对标签的内容进行解析并进行相应处理的手段。

注解是java中的一个重要知识点,从java5后开始引入,尤其在spring框架中大量使用。比较常用的有@controller、@service等等各种,本文将从注解的实现原理出发,通过一些demo代码的实现,进行分析。

一、 注解定义方式

直接上代码,看看spring中@Service注解的定义就知道了:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {

 String value() default "";

}

可以看到注解的定义和接口定义很像,但是多了@字符,注解的定义上有以下约定:

  • 只能定义属性名,不能定义方法
  • 属性的可见性只有public和default,不写则默认后者
  • 属性的类型只能支持:基本数据类型、string、class、enum、Annotation类型及以上类型的数组
  • 可以加上defult关键字指明默认值,当某字段不指明默认值时,必须在进行注解标注的时候进行此字段值的指定。
  • 当使用value作为属性名称时,可以不显式指定value=“xxx”,如可以直接使用@Service("xxxService")

二、元注解

所谓元注解就是java中默认实现的专门对注解进行注解的注解。元注解的总数就5个,下面我们以上面讲到的@Service注解为例子各个击破:

1.@Target

此注解用于表示当前注解的使用范围,@Target({ElementType.TYPE})就代表着@Service这个注解是专门用来注解到类、接口、或者枚举类型上面的,当在方法上面加这个注解时,就会报错。可以看到注解位置是一个枚举类型,完整定义如下

public enum ElementType {
  /** Class, interface (including annotation type), or enum declaration */
  TYPE,

  /** Field declaration (includes enum constants) */
  FIELD,

  /** Method declaration */
  METHOD,

  /** Formal parameter declaration */
  PARAMETER,

  /** Constructor declaration */
  CONSTRUCTOR,

  /** Local variable declaration */
  LOCAL_VARIABLE,

  /** Annotation type declaration */
  ANNOTATION_TYPE,

  /** Package declaration */
  PACKAGE,

  /**
   * Type parameter declaration
   *
   * @since 1.8
   */
  TYPE_PARAMETER,

  /**
   * Use of a type
   *
   * @since 1.8
   */
  TYPE_USE
}

2.@Retention

此注解用于表示当前注解的生命周期,说人话就是这个注解作用会保留到什么时候,如@Retention(RetentionPolicy.RUNTIME)就表示在程序运行期间依然有效,此时就可以通过反射拿到注解的信息,完整的枚举定义如下

public enum RetentionPolicy {
  /**
   * Annotations are to be discarded by the compiler.
   */
  SOURCE,

  /**
   * Annotations are to be recorded in the class file by the compiler
   * but need not be retained by the VM at run time. This is the default
   * behavior.
   */
  CLASS,

  /**
   * Annotations are to be recorded in the class file by the compiler and
   * retained by the VM at run time, so they may be read reflectively.
   *
   * @see java.lang.reflect.AnnotatedElement
   */
  RUNTIME
}

3.@Documented

当被此注解所注解时,使用javadoc工具生成文档就会带有注解信息。

4.@Inherited

此注解与继承有关,当A注解添加此注解后,将A注解添加到某类上,此类的子类就会继承A注解。

@Inherited
public @interface A{
}

@A
public class Parent{}

public class Son entends Parant{}//Son类继承了父类的A注解

5.@Repeatable

此注解顾名思义是拥有可以重复注解的能力。想象这样一个场景,我们需要定时执行某个任务,需要在每周一和周三执行,并且这个时间是可以灵活调整的,此时这个元注解就能派上用场:

@Repeatable(Schedules.class)
public @interface Schedule {
  String date();
}

public @interface Schedules {
  Schedule[] value();
}

@Schedule(date = "周一")
@Schedule(date = "周三")
public class Executor {
}

注意看到此元注解后面括号里内容,在这指定的类叫做容器注解,意思是保存这多个注解的容器,故我们创建一个@Schedules注解作为@Schedule的容器注解,容器注解必须含有一个名字为value,返回类型为需放入此容器的注解数组的属性。

三、自定义实现一个注解

下面我们以web项目中非常常见的鉴权场景为例自己实现一个自定义注解。

首先我们定义系统的使用人员身份,有超级管理员、管理员、访客三种身份。

public enum IdentityEnums {

  SUPER_ADMIN,
  ADMIN,
  VISIVOR

}

接下来我们定义一个权限注解:

@Target({ElementType.TYPE,ElementType.METHOD})
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Authorization {

  IdentityEnums[] value();

}

然后使用拦截器的方式,对所有页面进行统一的鉴权管理,此处只展示一些关键代码:

public class AuthInterceptor implements HandlerInterceptor {

  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

    if (handler instanceof HandlerMethod)
    {
      IdentityEnums user = getIdentityFromRequset(request);//这里从request里获取账号信息并判断身份,自己实现
      Authorization auth =((HandlerMethod) handler).getMethodAnnotation(Authorization.class);//获取方法上面的注解
      if (!Arrays.asList(auth.value()).contains(user)){
        return false;
      }
    }
    return true;
  }
}

最后在spring配置文件中对拦截器进行配置开启拦截器:

<!-- 拦截器 -->
 <mvc:interceptors>
 <mvc:interceptor>
  <!-- 匹配的是url路径, 如果不配置或/**,将拦截所有的Controller -->
  <mvc:mapping path="/**" />
  <!-- 拦截器类 -->
  <bean
  class="com.xx.xx.AuthInterceptor"></bean>
 </mvc:interceptor>
 </mvc:interceptors>

在实际使用中,我们将在方法上面添加此自定义注解,当身份权限符合时,才能对页面进行访问,使用方式如下:
@ResponseBody

@RequestMapping(value = "/management")
@Authorization({IdentityEnums.ADMIN,IdentityEnums.SUPER_ADMIN})
public String management(HttpServletRequest request, HttpServletResponse response)
{
  log.info("has permission!");
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 深入理解Java注解类型(@Annotation)

    Java注解是在JDK5时引入的新特性,鉴于目前大部分框架(如spring)都使用了注解简化代码并提高编码的效率,因此掌握并深入理解注解对于一个Java工程师是来说是很有必要的事.本篇我们将通过以下几个角度来分析注解的相关知识点 理解Java注解 实际上Java注解与普通修饰符(public.static.void等)的使用方式并没有多大区别,下面的例子是常见的注解: public class AnnotationDemo { //@Test注解修饰方法A @Test public static

  • 5分钟搞懂java注解@Annotation的具体使用

    首先一句话结论:注解就是一种通过在类.方法.或者属性等上使用类似@xxx的方式进行"打标签",然后可以通过反射机制对标签的内容进行解析并进行相应处理的手段. 注解是java中的一个重要知识点,从java5后开始引入,尤其在spring框架中大量使用.比较常用的有@controller.@service等等各种,本文将从注解的实现原理出发,通过一些demo代码的实现,进行分析. 一. 注解定义方式 直接上代码,看看spring中@Service注解的定义就知道了: @Target({El

  • 一文搞懂Java中的注解和反射

    目录 1.注解(Annotation) 1.1 什么是注解(Annotation) 1.2 内置注解 1.3 元注解(meta-annotation) 1.4 自定义注解 2.反射(Reflection) 2.1 反射和反射机制 2.2 Class类的获取方式和常用方法 2.3 反射的使用 1.注解(Annotation) 1.1 什么是注解(Annotation) 注解不是程序本身,可以在程序编译.类加载和运行时被读取,并执行相应的处理.注解的格式为"@注释名(参数值)",可以附加在

  • 3分钟快速搞懂Java的桥接方法示例

    什么是桥接方法? Java中的桥接方法(Bridge Method)是一种为了实现某些Java语言特性而由编译器自动生成的方法. 我们可以通过Method类的isBridge方法来判断一个方法是否是桥接方法. 在字节码文件中,桥接方法会被标记为ACC_BRIDGE和ACC_SYNTHETIC,其中ACC_BRIDGE用于表示该方法是由编译器产生的桥接方法,ACC_SYNTHETIC用于表示该方法是由编译器自动生成. 什么时候生成桥接方法? 为了实现哪些Java语言特性会生成桥接方法?最常见的两种

  • 一文搞懂Java中的反射机制

    前一段时间一直忙,所以没什么时间写博客,拖了这么久,也该更新更新了.最近看到各种知识付费的推出,感觉是好事,也是坏事,好事是对知识沉淀的认可与推动,坏事是感觉很多人忙于把自己的知识变现,相对的在沉淀上做的实际还不够,我对此暂时还没有什么想法,总觉得,慢慢来,会更快一点,自己掌握好节奏就好. 好了,言归正传. 反射机制是Java中的一个很强大的特性,可以在运行时获取类的信息,比如说类的父类,接口,全部方法名及参数,全部常量和变量,可以说类在反射面前已经衣不遮体了(咳咳,这是正规车).先举一个小栗子

  • 一篇文章带你搞懂Java线程池实现原理

    目录 1. 为什么要使用线程池 2. 线程池的使用 3. 线程池核心参数 4. 线程池工作原理 5. 线程池源码剖析 5.1 线程池的属性 5.2 线程池状态 5.3 execute源码 5.4 worker源码 5.5 runWorker源码 1. 为什么要使用线程池 使用线程池通常由以下两个原因: 频繁创建销毁线程需要消耗系统资源,使用线程池可以复用线程. 使用线程池可以更容易管理线程,线程池可以动态管理线程个数.具有阻塞队列.定时周期执行任务.环境隔离等. 2. 线程池的使用 /** *

  • 一文搞懂Java中的日期类

    目录 一.日期类 1.1 第一代日期类 1.2 第二代日期类Calendar 1.3 第三代日期类 一.日期类 在程序的开发中我们经常会遇到日期类型的操作,Java对日期类型的操作提供了很好的支持.在最初的版本下,java.lang包中的System.currentTimeMillis();可以获取当前时间与协调时间(UTC)1970年1月1日午夜之间的时间差(以毫秒为单位测量).我们往往通过调用该方法计算某段代码的耗时. public class TestTime { public stati

  • Java注解Annotation与自定义注解详解

    一:Java注解简介 开发中经常使用到注解,在项目中也偶尔会见到过自定义注解,今天就来探讨一下这个注解是什么鬼,以及注解的应用场景和如何自定义注解. 下面列举开发中常见的注解 @Override:用于标识该方法继承自超类, 当父类的方法被删除或修改了,编译器会提示错误信息(我们最经常看到的toString()方法上总能看到这货) @Deprecated:表示该类或者该方法已经不推荐使用,已经过期了,如果用户还是要使用,会生成编译的警告 @SuppressWarnings:用于忽略的编译器警告信息

  • 一文搞懂JAVA 修饰符

    Java语言提供了很多修饰符,主要分为以下两类: 访问修饰符 非访问修饰符 修饰符用来定义类.方法或者变量,通常放在语句的最前端.我们通过下面的例子来说明: public class ClassName { // ... } private boolean myFlag; static final double weeks = 9.5; protected static final int BOXWIDTH = 42; public static void main(String[] argum

  • 一文搞懂JAVA 枚举(enum)

    Java 枚举是一个特殊的类,一般表示一组常量,比如一年的 4 个季节,一个年的 12 个月份,一个星期的 7 天,方向有东南西北等. Java 枚举类使用 enum 关键字来定义,各个常量使用逗号 , 来分割. 例如定义一个颜色的枚举类. enum Color { RED, GREEN, BLUE; } 以上枚举类 Color 颜色常量有 RED, GREEN, BLUE,分别表示红色,绿色,蓝色. 使用实例: enum Color { RED, GREEN, BLUE; } public c

  • 两个小例子轻松搞懂 java 中递归与尾递归的优化操作

    废话不多说,我们直接上两个最常见的小例子: 一.递归,伪递归,迭代实现n! package com.njbdqn.test02; /** * 递归,伪递归,迭代实现n! */ public class RecursionTest { public static void main(String[] args) { System.out.println(recurse(5)); //递归显示 System.out.println(camouflageRecurse(5, 1)); //伪递归 Sy

随机推荐