自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. [19/04/17-星期三] Java的动态性_反射(Reflection)机制

    一.前言 动态语言:程序运行时,可以改变程序结构或变量类型.典型的代表:Python,ruby,JavaScript 如JavaScript代码: function test(){ var s=&qu ...

  2. Markdown语法初体验

    前言 由于把博客主题样式换了,所以改用Markdown语法,让代码看起来更加舒服一些. 照葫芦画瓢 这里是H1标题(===) 这里是H2标题(---) 使用一个#号 使用两个#号 使用三个#号 引用 ...

  3. background-clip 实现字体渐变效果

    background-clip 实现字体渐变效果 (一)类似KTV字幕效果 @-webkit-keyframes loop{ 0%{background-position: -800px 0;} 10 ...

  4. Spring(十四)之编程性事务(续)

    Spring 编程式事务管理 编程式事务管理方法允许你在对你的源代码编程的帮助下管理事务.这给了你极大地灵活性,但是它很难维护. 在我们开始之前,至少要有两个数据库表,在事务的帮助下我们可以执行多种 ...

  5. SVN篇

    启动SVN : svnserve -d -r svn 查看进程: ps -ef | grep svmserve -------------------------------------------- ...

  6. C#网络Socket编程

    1.什么是Socket Sockets 是一种进程通信机制,是一个通信链的句柄(其实就是两个程序通信用的) 2.分类 流式套接字(SOCK_STREAM):提供了一种可靠的.面向连接的双向数据传输服务 ...

  7. 前端基础-CSS是什么?

    阅读目录 一. 什么是CSS 二. 为何要用CSS 三. 如何使用CSS 一. 什么是CSS CSS全称Cascading Style Sheet层叠样式表,是专用用来为HTML标签添加样式的. 样式 ...

  8. C/S模式,发布/订阅模式和PUSH/PULL模式(上)

    CS模式(客户端/服务器模式) 最场景的信息传递模式,也称为Request/Response模式,或者调用模式.http/https协议即此模式.因为最常用所以大家一般都比较熟悉,这里不重点讲了,大家 ...

  9. html中的meta元素及viewport属性值

    <meta name="viewport" content="width=device-width , initial-scale=1.0, maximum-sca ...

  10. 常用LLDB指令

    print.p: 打印内存地址 po: 打印对象   1.读取内存 memory read/数量格式字节数 内存地址 x/数量格式字节数 内存x/3xw 0x10010 格式:x是16进制,f是浮点, ...