注解Annotation实现原理与自定义注解例子

?

什么是注解?

对于很多初次接触的开发者来说应该都有这个疑问?Annontation是Java5开始引入的新特征,中文名称叫注解。它提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联。为程序的元素(类、方法、成员变量)加上更直观更明了的说明,这些说明信息是与程序的业务逻辑无关,并且供指定的工具或框架使用。

Annontation像一种修饰符一样,应用于包、类型、构造方法、方法、成员变量、参数及本地变量的声明语句中。
  
Java注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。包含在 java.lang.annotation包中。

注解的用处:

1、生成文档。这是最常见的,也是java 最早提供的注解。常用的有@param @return 等
2、跟踪代码依赖性,实现替代配置文件功能。比如Dagger 2 依赖注入,未来java 开发,将大量注解配置,具有很大用处;
3、在编译时进行格式检查。如@override 放在方法前,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出。

注解的原理:

注解本质是一个继承了Annotation 的特殊接口,其具体实现类是Java 运行时生成的动态代理类。而我们通过反射获取注解时,返回的是Java 运行时生成的动态代理对象$Proxy1。通过代理对象调用自定义注解(接口)的方法,会最终调用AnnotationInvocationHandler 的invoke方法。该方法会从memberValues 这个Map 中索引出对应的值。而memberValues 的来源是Java 常量池。

元注解:

java.lang.annotation 提供了四种元注解,专门注解其他的注解(在自定义注解的时候,需要使用到元注解):

  • @Documented – 注解是否将包含在JavaDoc中
  • @Retention – 什么时候使用该注解
  • @Target – 注解用于什么地方
  • @Inherited – 是否允许子类继承该注解

[email protected] – 定义该注解的生命周期

  • RetentionPolicy.SOURCE : 在编译阶段丢弃。这些注解在编译结束之后就不再有任何意义,所以它们不会写入字节码。@Override@SuppressWarnings都属于这类注解。
  • RetentionPolicy.CLASS : 在类加载的时候丢弃。在字节码文件的处理中有用。注解默认使用这种方式
  • RetentionPolicy.RUNTIME : 始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。我们自定义的注解通常使用这种方式。

2.Target – 表示该注解用于什么地方。默认值为任何元素,表示该注解用于什么地方。可用的ElementType 参数包括

  • ElementType.CONSTRUCTOR: 用于描述构造器
  • ElementType.FIELD: 成员变量、对象、属性(包括enum实例)
  • ElementType.LOCAL_VARIABLE: 用于描述局部变量
  • ElementType.METHOD: 用于描述方法
  • ElementType.PACKAGE: 用于描述包
  • ElementType.PARAMETER: 用于描述参数
  • ElementType.TYPE: 用于描述类、接口(包括注解类型) 或enum声明

[email protected] – 一个简单的Annotations 标记注解,表示是否将注解信息添加在java 文档中。

[email protected] – 定义该注释和子类的关系

@Inherited 元注解是一个标记注解,@Inherited 阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited 修饰的annotation 类型被用于一个class,则这个annotation 将被用于该class 的子类。

常见标准的Annotation:

1.Override

java.lang.Override是一个标记类型注解,它被用作标注方法。它说明了被标注的方法重载了父类的方法,起到了断言的作用。如果我们使用了这种注解在一个没有覆盖父类方法的方法时,java 编译器将以一个编译错误来警示。

2.Deprecated

Deprecated 也是一种标记类型注解。当一个类型或者类型成员使用@Deprecated 修饰的话,编译器将不鼓励使用这个被标注的程序元素。所以使用这种修饰具有一定的“延续性”:如果我们在代码中通过继承或者覆盖的方式使用了这个过时的类型或者成员,虽然继承或者覆盖后的类型或者成员并不是被声明为@Deprecated,但编译器仍然要报警。

3.SuppressWarnings

SuppressWarning 不是一个标记类型注解。它有一个类型为String[] 的成员,这个成员的值为被禁止的警告名。对于javac 编译器来讲,被-Xlint 选项有效的警告名也同样对@SuppressWarings 有效,同时编译器忽略掉无法识别的警告名。
  
@SuppressWarnings("unchecked")

自定义注解:

自定义注解类编写的一些规则:

  1. Annotation 型定义为@interface, 所有的Annotation 会自动继承java.lang.Annotation这一接口,并且不能再去继承别的类或是接口.
  2. 参数成员只能用public 或默认(default) 这两个访问权修饰
  3. 参数成员只能用基本类型byte、short、char、int、long、float、double、boolean八种基本数据类型和String、Enum、Class、annotations等数据类型,以及这一些类型的数组.
  4. 要获取类方法和字段的注解信息,必须通过Java的反射技术来获取 Annotation 对象,因为你除此之外没有别的获取注解对象的方法
  5. 注解也可以没有定义成员,不过这样注解就没啥用了

PS:自定义注解需要使用到元注解

自定义注解实例:

FruitName.java

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * 水果名称注解
 */
@Target(FIELD)
@Retention(RUNTIME)
@Documented
public @interface FruitName {
    String value() default "";
}

FruitColor.java

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * 水果颜色注解
 */
@Target(FIELD)
@Retention(RUNTIME)
@Documented
public @interface FruitColor {
    /**
     * 颜色枚举
     */
    public enum Color{ BLUE,RED,GREEN};

    /**
     * 颜色属性
     */
    Color fruitColor() default Color.GREEN;

}

FruitProvider.java

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * 水果供应者注解
 */
@Target(FIELD)
@Retention(RUNTIME)
@Documented
public @interface FruitProvider {
    /**
     * 供应商编号
     */
    public int id() default -1;

    /**
     * 供应商名称
     */
    public String name() default "";

    /**
     * 供应商地址
     */
    public String address() default "";
}

FruitInfoUtil.java

import java.lang.reflect.Field;

/**
 * 注解处理器
 */
public class FruitInfoUtil {
    public static void getFruitInfo(Class<?> clazz){

        String strFruitName=" 水果名称:";
        String strFruitColor=" 水果颜色:";
        String strFruitProvicer="供应商信息:";

        Field[] fields = clazz.getDeclaredFields();

        for(Field field :fields){
            if(field.isAnnotationPresent(FruitName.class)){
                FruitName fruitName = (FruitName) field.getAnnotation(FruitName.class);
                strFruitName=strFruitName+fruitName.value();
                System.out.println(strFruitName);
            }
            else if(field.isAnnotationPresent(FruitColor.class)){
                FruitColor fruitColor= (FruitColor) field.getAnnotation(FruitColor.class);
                strFruitColor=strFruitColor+fruitColor.fruitColor().toString();
                System.out.println(strFruitColor);
            }
            else if(field.isAnnotationPresent(FruitProvider.class)){
                FruitProvider fruitProvider= (FruitProvider) field.getAnnotation(FruitProvider.class);
                strFruitProvicer=" 供应商编号:"+fruitProvider.id()+" 供应商名称:"+fruitProvider.name()+" 供应商地址:"+fruitProvider.address();
                System.out.println(strFruitProvicer);
            }
        }
    }
}

Apple.java

import test.FruitColor.Color;

/**
 * 注解使用
 */
public class Apple {

    @FruitName("Apple")
    private String appleName;

    @FruitColor(fruitColor=Color.RED)
    private String appleColor;

    @FruitProvider(id=1,name="陕西红富士集团",address="陕西省西安市延安路89号红富士大厦")
    private String appleProvider;

    public void setAppleColor(String appleColor) {
        this.appleColor = appleColor;
    }
    public String getAppleColor() {
        return appleColor;
    }

    public void setAppleName(String appleName) {
        this.appleName = appleName;
    }
    public String getAppleName() {
        return appleName;
    }

    public void setAppleProvider(String appleProvider) {
        this.appleProvider = appleProvider;
    }
    public String getAppleProvider() {
        return appleProvider;
    }

    public void displayName(){
        System.out.println("水果的名字是:苹果");
    }
}

FruitRun.java

/**
 * 输出结果
 */
public class FruitRun {
    public static void main(String[] args) {
        FruitInfoUtil.getFruitInfo(Apple.class);
    }
}

运行结果是:

水果名称:Apple
水果颜色:RED
供应商编号:1 供应商名称:陕西红富士集团 供应商地址:陕西省西安市延安路89号红富士大厦

扩展阅读

Spring注解@Resource和@Autowired区别对比

springBoot注解大全

Web应用运行原理

对HashMap的思考及手写实现

面试时,如何在1分钟内更好的展现自己?

作者:贾树丙

来源:https://www.cnblogs.com/acm-bingzi/p/javaAnnotation.html

原文地址:https://www.cnblogs.com/javafirst0/p/11224749.html

时间: 2024-10-29 00:25:27

注解Annotation实现原理与自定义注解例子的相关文章

Java基础笔记 – Annotation注解的介绍和使用 自定义注解

Java基础笔记 – Annotation注解的介绍和使用 自定义注解 本文由arthinking发表于5年前 | Java基础 | 评论数 7 |  被围观 25,969 views+ 1.Annotation的工作原理:2.@Override注解:3.@Deprecated注解:4.@SuppressWarnings注解:5.自定义注解:5.1.添加变量:5.2.添加默认值:5.3.多变量使用枚举:5.4.数组变量:6.设置注解的作用范围:6.1.在自定义注解中的使用例子:7.使用反射读取R

跟王老师学注解(四):自定义注解

跟王老师学注解(四):自定义注解 主讲教师:王少华   QQ群号:483773664 到此,我们已经学习完了JDK提供的3种内建注解及4种元注解,下面来了解自定义注解 一.注解类型 (一)注解类型与接口:注解类型是一种接口,但它又不同于接口 1.注解类型使用关键字@interface而不是interface 1 2 public @interface TestAno { } @interface隐含继承java.lang.annotation.Annotation接口 2.注解类型的方法定义是独

spring中自定义注解(annotation)与AOP中获取注解

一.自定义注解(annotation) 自定义注解的作用:在反射中获取注解,以取得注解修饰的类.方法或属性的相关解释. package me.lichunlong.spring.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.R

自定义注解,通过反射获得注解中的值(详细自定义注解解释)

自定义注解(@Alias): package com.nf.lc.demo3; import java.lang.annotation.*; /* 定义注解的生命周期元注解:@Retention RetentionPolicy.SOURCE 在编译阶段丢弃,编译结束没有任何意义 RetentionPolicy.CLASS 在类加载时丢弃,字节码文件处理时有用,默认这种方式 ☆ RetentionPolicy.RUNTIME 始终不会丢弃,运行时期也保留该注解,可以通过反射机制读取该信息 */ @

使用Java反射(Reflect)、自定义注解(Customer Annotation)生成简单SQL语句

这次给大家介绍一下在Java开发过程中 使用自定义注解开发:主要知识点:            1.反射            主要用于提取注解信息            2.自定义异常  主要是为了自己自定义一个异常信息            3.自定义注解  本次重点 学会如何自定义注解以及如何使用反射提取注解信息运用到实际开发下图表示在Java中注解的含义以及注解的分类和如何解析注解 通常我们使用自定义注解一般使用4中元注解即:@Target@Retention@Documented@In

Java反射reflection与注解annotation的应用(自动测试机)

一.关于自动测试机 1.什么是自动测试机? 对类中的指定方法进行批量测试的工具 2.自动测试机又什么用? a.避免了冗长的测试代码 当类中的成员方法很多时,对应的测试代码可能会很长,使用测试能够让测试代码非常简洁 b.降低了类与测试代码之间的耦合性 以防万一,测试时应该对类中所有方法进行测试.当我们对类中的一个方法做改动时,很可能我们的测试代码也要做相应改动(如改变了方法名,返回值类型等等),使用测试机能降低类与测试代码之间的耦合性,大大降低不必要的工作量. c.增加了测试的灵活性 测试类与待测

Java千百问_03基础语法(021)_如何自定义注解

点击进入_更多_Java千百问color="#ff0000" 1.如何自定义注解 要学习使用注解,我们需要了解注解,并且了解java提供的常见注解.更重要的是学会自定义注解. 了解注释看这里:注释是什么 我们自定义注解需要使用@interface关键字,具体语法如下: public @interface 注解名 {定义体} default {默认值} 使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其

深入 Java自定义注解

我们在使用Spring框架的时候,会经常使用类似:@Autowired 这样的注解.我们也可以自己定义一些注解.Java的注解主要在包:java.lang.annotation中实现. 1. 元注解 什么是元注解?你可以这样理解,元注解是自定义注解的注解.元注解主要包含4个.他们主要在java.lang.annotation中可以找到.我们自己要创建注解的时候必须要用到这些元注解.所以必须彻底理解这四个元注解的含义. 1. @Documented 2. @Inherited 3. @Retent

java中的注解(Annotation)

转载:https://segmentfault.com/a/1190000007623013 简介 注解,java中提供了一种原程序中的元素关联任何信息.任何元素的途径的途径和方法. 注解是那些插入到源代码中使用其他工具可以对其进行处理的标签.注解不会改变程序的编译方式.java编译器会对包含注解与不包含注解的代码生成相同的虚拟机指令.在java中,注解是被当做修饰符(如public/static之类)来使用的. 注解与注释 注释是供人看的,注解是供程序调用的.一种是程序员写给另一个程序员的,一