自JDK1.5之后,就开始出现注解。想要了解注解的来源和注解的用法,通过搜索引擎大都是针对某一个注解的解释,很难找到关于注解系列的文章,便自己看下。

  基于Annotation的注释,说明Annotaion是所有注解类型扩展的公共接口。当自定义为@inferface便实现该接口。可用 instanceof去校验@interface是否属于Annotation.而上面的Target属于注解其它的注解的元注解。元注解有四个:Target、Retention、Documented、

Inherited。

  Target

    TYPE: 类
    FIELD: 字段
    METHOD: 方法
    PARAMETER: 参数
    CONSTRUCTOR: 构造函数
    LOCAL_VARIABLE:本地变量
    ANNOTATION_TYPE:注解
    PACKAGE:包

  现在演示一下Target

  自定一个注解,作用于FIELD的:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AnnotationTarget {
String[] value() default "iamsb";
}

  被标记的字段拥有了超能力,是的,其它的字段仍然很傻,以下是被标记的字段赋予超能力的过程

public class AnnotationTargetProcessor {

    public static void process(Class clazz, Object obj) {
Field[] fields = clazz.getDeclaredFields();
for(Field field : fields) {
if(field.isAnnotationPresent(AnnotationTarget.class)){
try {
field.setAccessible(true); // 被注解的字段是private ,所以设置true
field.set(obj, "now,i am still sb");
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}

  最后的测试类

  

public class SBVO {
private String a; private String b; @AnnotationTarget("zz")
private String c; private Integer d; private Boolean bool = false; public static void main(String[] args) {
SBVO sv = new SBVO();
AnnotationTargetProcessor.process(SBVO.class,sv);
String c = sv.c;
String b = sv.b;
System.out.println( "c ==== " + c); // c ==== now,i am still sb
System.out.println( "b ==== " + b); // b ==== null
}
}
说明:1.@AnnotationTarget声明的是@Target(ElementType.FIELD),所以在用注解的时候,只能作用于字段上,而当你把@AnnotationTarget("zz")想放在类上面时,编译都不让你过,提示:The annotation @AnnotationTarget is disallowed for this location
   2.注解本身不具备超能力,它本身属于被动的。而是我们的规则处理类发现某个元素有某种注解,就按照某种规则来给这个元素赋予某种能力。比如我上面的AnnotationTargetProcessor扫描到字段c 有个AnnotationTarget注解,那我就给与你什么能力。同理,springboot的
    某个类(目前不知道是哪个)发现有个类被标记了@Service,那我就把它来赋予Service能力。
   3.@interface里面只能定义返回类型为 所有基本类型、String、enum、Annotation、Class和 它们的数组。
   4.用注解的时候,跟我们调用方法一样 注解名(元素名=元素值),比如我们springboot的启动类 @SpringBootApplication(scanBasePackages = "xxxx",exclude={xxx.class,yyy.class}) ,可以查到SpringBootApplication注解下scanBasePackages和exclude
    元素。
   5.@AnnotationTarget("zz")这里要说一下,这个原本是这样:@AnnotationTarget("value=zz"),如果声明的注解里只对value赋值,那么在引用的时候value=可以省略。

  接着再看下元注解

  Documented

  它没有元素,所以也被称为标记注解,我在SBVO中添加一行代码:

 

@AnnotationTarget("nc")
public static final String e = "SPRING SUMMER";

  然后用javadoc  -encoding UTF-8 AnnotationTarget.java SBVO.java 生成文档,这里我的类里面有中文,所以添加了utf8方式,生成的doc文档,入口是index.html,见下图:

  就是标红椭圆的注解是否被添加到doc文档的差别。如果没有添加元注解Documented,这里是没有红色椭圆的文本信息的。所以定义了Documented的自定义注解,在生成javadoc文档时,就会添加该注解文本信息。

  接着看

  Retention

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
}

  1. SOURCE ,编译器将丢弃注解

  2. CLASS,编译器将在类文件中记录注解,但是不需要在运行时被VM保留。这是默认值的行为。

  3. RUNTIME,编译器将在类文件中记录注解,并且在运行时被VM保留。因此在反射时可以读取得到它们。

  作何解释呢?我先把我自定义注解AnnotationTarget的保留策略改成RetentionPolicy.SOURCE

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
public @interface AnnotationTarget {
String[] value() default "iamsb";
}

  仍然用下面这个类来引用它

public class SBVO {
private String a; private String b; @AnnotationTarget("zz")
private String c; private Integer d; private Boolean bool = false; @AnnotationTarget("nc")
public static final String e = "SPRING SUMMER"; @AnnotationTarget("")
private String f = "i do not want to be a sb."; public static void main(String[] args) {
SBVO sv = new SBVO();
AnnotationTargetProcessor.process(SBVO.class,sv);
String c = sv.c;
String b = sv.b;
String f = sv.f;
System.out.println( "c ==== " + c);
System.out.println( "b ==== " + b);
System.out.println( "e ==== " + e);
System.out.println( "f ==== " + f);
}
}

  经过编译后,把引用的SBVO.class反编译,可以看到

public class SBVO
{
private String a;
private String b;
private String c; // 未保留
private Integer d;
private Boolean bool = Boolean.valueOf(false);
public static final String e = "SPRING SUMMER"; // 未保留
private String f = "i do not want to be a sb."; // 未保留 public static void main(String[] args)
{
SBVO sv = new SBVO();
AnnotationTargetProcessor.process(SBVO.class, sv);
String c = sv.c;
String b = sv.b;
String f = sv.f;
System.out.println("c ==== " + c);
System.out.println("b ==== " + b);
System.out.println("e ==== SPRING SUMMER");
System.out.println("f ==== " + f);
}
}

  这里就解释了策略为SOURCE时,编译后就把注解丢弃了。

  当我把策略改成RetentionPolicy.CLASS,编译后再来看SBVO.class

public class SBVO
{
private String a;
private String b;
@AnnotationTarget({"zz"})
private String c; // 保留了注解
private Integer d;
private Boolean bool = Boolean.valueOf(false);
@AnnotationTarget({"nc"})
public static final String e = "SPRING SUMMER"; // 保留了注解
@AnnotationTarget({""})
private String f = "i do not want to be a sb."; // 保留了注解 public static void main(String[] args)
{
SBVO sv = new SBVO();
AnnotationTargetProcessor.process(SBVO.class, sv);
String c = sv.c;
String b = sv.b;
String f = sv.f;
System.out.println("c ==== " + c);
System.out.println("b ==== " + b);
System.out.println("e ==== SPRING SUMMER");
System.out.println("f ==== " + f);
}
}

  此时我来运行SBVO的main 方法,得到结果:

    c ==== null
  b ==== null
  e ==== SPRING SUMMER
  f ==== i do not want to be a sb.

  此时main方法里面的AnnotationTargetProcessor.process(SBVO.class, sv); 其中process方法if(field.isAnnotationPresent(AnnotationTarget.class)) 判断始终是false,反射取不到,说明注解没在虚拟机中驻留。所以,c、b、e、f都是原值,那我在把自定义注解

AnnotationTarget策略更改成RetentionPolicy.RUNTIME,首先注解时保留在SBVO.class文件中的
  @AnnotationTarget({"zz"})
private String c; // 保留
private Integer d;
private Boolean bool = Boolean.valueOf(false);
@AnnotationTarget({"nc"})
public static final String e = "SPRING SUMMER"; // 保留
@AnnotationTarget({""})
private String f = "i do not want to be a sb."; // 保留

然后,运行SBVO中的main方法,结果为:

  java.lang.IllegalAccessException: Can not set static final java.lang.String field studiii.zlsj_test.annotation.target.SBVO.e to java.lang.String
        at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(Unknown Source)
        at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(Unknown Source)
        at sun.reflect.UnsafeQualifiedStaticObjectFieldAccessorImpl.set(Unknown Source)
        at java.lang.reflect.Field.set(Unknown Source)
        at studiii.zlsj_test.annotation.target.AnnotationTargetProcessor.process(AnnotationTargetProcessor.java:19)
        at studiii.zlsj_test.annotation.target.SBVO.main(SBVO.java:30)
  c ==== now,i am still sb                          // c 由空值改成当前值
  b ==== null                                             // b 未添加注解
  e ==== SPRING SUMMER                    // final 改不了,上面也抛异常了
  f ==== now,i am still sb                          // f 值由 i do not want to be a sb.改成 now,i am still sb

此时看到AnnotationTargetProcessor.process通过反射把标记了注解的字段都更改了,e标记了注解,但e是final变量,所以当想要更改它时,是不允许的。

接着看最后一个
  Inherited
  从它的源码可以看到,它没有定义任何元素,所以它和Documented一样,属于标记注解。再来看一下它的注释:
/**
* Indicates that an annotation type is automatically inherited. If
* an Inherited meta-annotation is present on an annotation type
* declaration, and the user queries the annotation type on a class
* declaration, and the class declaration has no annotation for this type,
* then the class's superclass will automatically be queried for the
* annotation type. This process will be repeated until an annotation for this
* type is found, or the top of the class hierarchy (Object)
* is reached. If no superclass has an annotation for this type, then
* the query will indicate that the class in question has no such annotation.
*
* <p>Note that this meta-annotation type has no effect if the annotated
* type is used to annotate anything other than a class. Note also
* that this meta-annotation only causes annotations to be inherited
* from superclasses; annotations on implemented interfaces have no
* effect.
*
* @author Joshua Bloch
* @since 1.5
* @jls 9.6.3.3 @Inherited
*/

  翻译一下:表明它是一个自动继承的注解类。如果一个声明的注解类(自定义注解)上存在这个继承元注解,并且用户去查找某个声明类上的自定义注解,并且这个声明类没有这个自定义注解,那么将自动去搜索这个类的父类的自定义注解。这个过程将

       重复,直到这个自定义注解被找到,或者搜索直到这个声明类的最顶层Object类。如果没有父类(这里指父、祖父、曾祖父直到Object)含有自定义注解,那么就表明这个声明类没有这个自定义注解。

       注意,如果自定义注解不是注释在一个类上的话,这个元注解(指Inherited)是没有效果的。还注意,这个元注解只会导致注解被继承自超类,实现接口上的注解是无效的。

  再翻译一下:1.这是一个自动继承的元注解,它是注解自定义注解的注解。

        2.如果某个类的父类、或者祖父类等有@Inherited注解或含有@Inherited的自定义注解,那么子类便可继承父类的注解。

        3.该注解只对类有效,对接口实现是无效的。

  例子:

  创建一个注解,标记Inherited

@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface InheAnnoTest {
String value() default "ni da ye";
}

  创建一个父类

@InheAnnoTest("Just Parent")
public class ParentClass { private String name ; @AnnotationTarget("yes, i am 15")
String age; @MethodTestAnno("yeah")
public void attack() { }
@InheAnnoTest("ojbk")
protected String plus() {
return "";
}
}

  创建一个子类

public class ChildClass extends ParentClass {

    private String name ;

    String age;

    public void attack() {

    }

    protected String plus() {
return "plus";
} }

  测试类

public class Test {

    /**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Class<ChildClass> cc = ChildClass.class;
Class<ParentClass> pc = ParentClass.class;
System.out.println(pc.isAnnotationPresent(InheAnnoTest.class)); // true
System.out.println(cc.isAnnotationPresent(InheAnnoTest.class)); // true
Annotation[] annos = cc.getAnnotations();
for(Annotation anno : annos) {
System.out.println(anno.annotationType().getName()); // studiii.zlsj_test.annotation.inheritedTest.InheAnnoTest
}
} }

  如果ChildClass和ParentClass 无继承关系,那么

    System.out.println(cc.isAnnotationPresent(InheAnnoTest.class));         // false
 并且 annos 就为空了。
 再来验证接口,创建一个接口:
  
@InheAnnoTest("")                                                            // 打上了标记
public interface ParentInterface { public static final String name = "daye"; void atack();
}

  实现类:

public class ChildInterface implements ParentInterface{

    @Override
public void atack() { }
}

  得到结果:

Class<ChildInterface> ci = ChildInterface.class;
System.out.println(ci.isAnnotationPresent(InheAnnoTest.class)); // false
  因此,就印证了Inherited注释里得说明。
  总结一下:注解Annotation 从JDK1.5之后被引入。它包含了4个元注解:Tagert、Documented、 Retention、 Inherited,元注解的意思就是可以注解其它注解的注解。就好比我们拼音有6个元音 a、o、e、i、u、u(..),可以组成很多双韵母,比如ai、ei、ui、ao、ou等等,进而拼出
       中华上下五千年沉淀的各种汉字。同理,4个元注解也可衍生出各种功能的注解。
       Tagert注解指明你定义的注解的作用域,是Field、Method还是Type等。
       Documented注解表示通过javadoc后,会再doc中显示出来。
       Retention注解,表示注解的保留策略,仅有三种,1.编译时就把注解丢弃。2.编译时保留,但再JVM中不保留。3.编译时保留,JVM中也保留,反射可从JVM中取到该注解。
       Inherited注解,表示子类可以取到父类中定义了该注解的注解。

 

Java 注解 初探 (一)的更多相关文章

  1. Java注解

    Java注解其实是代码里的特殊标记,使用其他工具可以对其进行处理.注解是一种元数据,起到了描述.配置的作用,生成文档,所有的注解都隐式地扩展自java.lang.annotation.Annotati ...

  2. 19.Java 注解

    19.Java注解 1.Java内置注解----注解代码 @Deprecated                                    //不推荐使用的过时方法 @Deprecated ...

  3. Java注解入门

    注解的分类   按运行机制分:   源码注解:只在源码中存在,编译后不存在 编译时注解:源码和编译后的class文件都存在(如@Override,@Deprecated,@SuppressWarnin ...

  4. java注解(Annotation)解析

    注解(Annotation)在java中应用非常广泛.它既能帮助我们在编码中减少错误,(比如最常见的Override注解),还可以帮助我们减少各种xml文件的配置,比如定义AOP切面用@AspectJ ...

  5. JAVA 注解的几大作用及使用方法详解

    JAVA 注解的几大作用及使用方法详解 (2013-01-22 15:13:04) 转载▼ 标签: java 注解 杂谈 分类: Java java 注解,从名字上看是注释,解释.但功能却不仅仅是注释 ...

  6. attilax.java 注解的本质and 使用最佳实践(3)O7

    attilax.java 注解的本质and 使用最佳实践(3)O7 1. 定义pojo 1 2. 建立注解By eclipse tps 1 3. 注解参数的可支持数据类型: 2 4. 注解处理器 2 ...

  7. paip.java 注解的详细使用代码

    paip.java 注解的详细使用代码 作者Attilax 艾龙,  EMAIL:1466519819@qq.com 来源:attilax的专栏 地址:http://blog.csdn.net/att ...

  8. JAVA 注解的几大作用及使用方法详解【转】

    java 注解,从名字上看是注释,解释.但功能却不仅仅是注释那么简单.注解(Annotation) 为我们在代码中添加信息提供了一种形式化的方法,是我们可以在稍后 某个时刻方便地使用这些数据(通过 解 ...

  9. 框架基础——全面解析Java注解

    为什么学习注解? 学习注解有什么好处? 学完能做什么? 答:1. 能够读懂别人写的代码,特别是框架相关的代码: 2. 让编程更加简洁,代码更加清晰: 3. 让别人高看一眼. spring.mybati ...

随机推荐

  1. Android攻城狮学习笔记—入门篇二

    第七章  跑马灯 activity_main.xml<LinearLayout xmlns:android="http://schemas.android.com/apk/res/an ...

  2. POJ 3762 The Bonus Salary!(最小K覆盖)

    POJ 3762 The Bonus Salary! 题目链接 题意:给定一些任务.每一个任务有一个时间,有k天.一个时间仅仅能运行一个任务,每一个任务有一个价值.问怎么安排能得到最多价值 思路:典型 ...

  3. Web—10-前端性能优化

    前端性能优化 从用户访问资源到资源完整的展现在用户面前的过程中,通过技术手段和优化策略,缩短每个步骤的处理时间从而提升整个资源的访问和呈现速度.网站的性能直接会影响到用户的数量,所有前端性能优化很重要 ...

  4. Oracle锁处理、解锁方法

    1.查询锁情况 select sid,serial#,event,BLOCKING_SESSION from v$session where event like '%TX%'; 2.根据SID查询具 ...

  5. SSM(SpringMVC+Spring+Mybatis)框架学习理解

    近期做到的项目中,用到的框架是SSM(SpringMVC+Spring+Mybatis).之前比较常见的是SSH.用到了自然得了解各部分的分工 spring mvc 是spring 处理web层请求的 ...

  6. bfs,队列

    bfs bfs=队列 队列的操作 头文件 #include<deque> 声明方法: 1.普通声明 queue<int>q; 2.结构体 struct node { int x ...

  7. CentOS7.6离线安装Tomcat8.5

    准备好tomcat安装文件: 官网下载apache-tomcat-8.5.39.tar.gz文件并复制到/usr/tomcat文件夹中. 解压tomcat安装文件: 进入/usr/tomcat文件:c ...

  8. ionic ios 打包发布流程

    1.ionic cordova resources ios    在windows下 生成ios资源包 2.拷贝ionic 项目到mac电脑 不用拷贝platforms 并解压 3.正常情况下wido ...

  9. 完美解决 Cydia 不能上网

    国行手机比美版.港版.韩版手机新增了网络授权的功能,iOS 10 及以上系统版本,任何应用首次打开,如果有请求网络的行为,都会提示网络请求授权的对话框. 首次打开 Cydia 并没有提示网络请求授权的 ...

  10. EDID的简介和解析

    去年对EDID做了一个解析,下面是学习EDID过程中整理的资料. 一.EDID简介 EDID: Extended Display Identification Data (外部显示设备标识数据)--- ...