专栏首页JAVA乐园java中注解的使用

java中注解的使用

0x01. 说在前面

使用注解开发的好处

1. 使代码更加干净易读,易于维护修改。比如,以前使用spring的开发,都是基于xml文件实现了统一的配置管理,但是缺点也是显而易见的,就是随着项目的越来越大,xml文件会变得越来越复杂,维护成本也会越来越高。使用注解就可以提供更大的便捷性,易于维护修改。

2. 可以实现代码的类型检查,特别是在编译器的角度实现一些类型检查,比如预检查(@Override)和抑制警告(@SuppressWarnings)等。

3. 自定义注解,作为额外信息的载体,存储有关程序的额外信息

0x02. 注解的分类以及使用

Java注解是附加在代码中的一些元信息,用于编译和运行时进行解析和使用,起到说明、配置的功能。

注解不会影响代码的实际逻辑,仅仅起到辅助性的作用。包含在java.lang.annotation包中。注解的定义类似于接口的定义,使用@interface来定义,定义一个方法即为注解类型定义了一个元素,方法的声明不允许有参数或throw语句,返回值类型被限定为原始数据类型、字符串String、Class、enums、注解类型,或前面这些的数组,方法可以有默认值。注解并不直接影响代码的语义,但是他可以被看做是程序的工具或者类库。它会反过来对正在运行的程序语义有所影响。注解可以从源文件、class文件或者在运行时通过反射机制多种方式被读取。

一般来说,注解一般分为三种类型:元注解,标准注解,自定义注解

2.1 元注解

元注解是专职负责注解其他注解,主要是标明该注解的使用范围,生效范围。我们是不能改变它的,只能用它来定义我们自定义的注解。

包括 @Retention @Target @Document @Inherited四种。(java.lang.annotation中提供,为注释类型)。

注解

说明

@Target

定义注解的作用目标,也就是可以定义注解具体作用在类上,方法上,还是变量上

@Retention

定义注解的保留策略。RetentionPolicy.SOURCE:注解仅存在于源码中,在class字节码文件中不包含;RetentionPolicy.CLASS:默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得;RetentionPolicy.RUNTIME:注解会在class字节码文件中存在,在运行时可以通过反射获取到。

@Document

说明该注解将被包含在javadoc中

@Inherited

说明子类可以继承父类中的该注解

Target类型主要依赖于ElementType这个类型,具体的类型如下:

Target类型

说明

ElementType.TYPE

接口、类、枚举、注解

ElementType.FIELD

字段、枚举的常量

ElementType.METHOD

方法

ElementType.PARAMETER

方法参数

ElementType.CONSTRUCTOR

构造函数

ElementType.LOCAL_VARIABLE

局部变量

ElementType.ANNOTATION_TYPE

注解

ElementType.PACKAGE

2.2 标准注解

Java标准注解提供了三个,定义在java.lang中的注解,我认为这三个注解的作用更多的是一种注释

  • @Override 表示当前方法覆盖父类中的方法。
  • @Deprecated 标记一个元素为已过期,避免使用

支持的元素类型为:CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE

  • @SuppressWarnings 不输出对应的编译警告

一个简单的使用demo:

@SuppressWarnings(value = {"unused", "rawtypes"})
public class Children  extends Parent{
    @Override
    public void work() {
        System.out.println("我是一个被重写的方法");
    }

    @Deprecated
    public void play(){
        System.out.println("这个方法不推荐使用了");
    }
}

2.3 自定义注解实现一个sql语句的拼接

需要注意的方面:注解的定义类似于接口的定义,使用@interface来定义,定义一个方法即为注解类型定义了一个元素,方法的声明不允许有参数或throw语句,返回值类型被限定为原始数据类型、字符串String、Class、enums、注解类型,或前面这些的数组,方法可以有默认值。

自定义注解一般可以分为三步:定义注解,使用注解,读取注解

定义注解

@Target(ElementType.TYPE) //注解加载类上
@Retention(RetentionPolicy.RUNTIME) // 运行时读取注解

public @interface Table {
    String value(); 
}


@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public@interface UserFiled {
    String name();
    String type();
    int length();
}

使用注解

// 将自定义的注解加在用户上,实现一个表的映射
@Table(value = "user_table")
public class User {

    @UserFiled(name = "user_id",type = "int",length = 8)
    private int userId;

    @UserFiled(name = "user_name",type = "varchar",length = 16)
    private String userName;

    @UserFiled(name = "password",type = "varchar",length = 16)
    private String password;

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

读取注解的内容

/**
 * 读取注解中的值
 */
public class GetUser {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class userClass = Class.forName("annocation.blog.User");

        // 读取类上的注解
        Table table = (Table) userClass.getAnnotation(Table.class);
        System.out.println(table.value());

        // 读取属性上注解
        Field userId = userClass.getDeclaredField("userId");
        UserFiled userFiledId = userId.getAnnotation(UserFiled.class);
        System.out.println(userFiledId.length() + "----" + userFiledId.type() + "-----" + userFiledId.name());

        Field userName = userClass.getDeclaredField("userName");
        UserFiled userFiledName = userName.getAnnotation(UserFiled.class);
        System.out.println(userFiledName.length()+"----"+userFiledName.type()+"----"+userFiledName.name());

        Field password = userClass.getDeclaredField("password");
        UserFiled userFiledPassword = password.getAnnotation(UserFiled.class);
        System.out.println(userFiledPassword.name() + "-----" + userFiledPassword.type() + "-----" + userFiledPassword.length());

        // 拼接一个sql语句
        String name = "chenwei";
        String sql ="select * from" + table.value()+"where"+userFiledName.name()+"="+name;
    }
}

结果:

user_table
user_id----int-----8
user_name----varchar----16
password-----varchar-----16

自定义注解的实现过程:

1. 定义注解

2. 使用注解,根据自己定义的注解来达到一些目的,本例中,就是使用注解来完成数据库表和实体类的映射关系

3. 读取注解的内容,也是比较重要的一部分,核心还是利用了反射的思想,得到使用注解的这个类,根据类中的getAnnotion的方法得到定义的注解,获得注解上的值。

0x03. 注解的实现的原理

注解的实现的原理很大的一部分是基于反射实现。

反射可以获取到Class对象,进而获取到Constructor、Field、Method等实例,点开源码结构发现Class、Constructor、Field、Method等均实现了AnnotatedElement接口,AnnotatedElement接口的方法如下

// 判断该元素是否包含指定注解,包含则返回true
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)

// 返回该元素上对应的注解,如果没有返回null
<T extends Annotation> T getAnnotation(Class<T> annotationClass);

// 返回该元素上的所有注解,如果没有任何注解则返回一个空数组
Annotation[] getAnnotations();

// 返回指定类型的注解,如果没有返回空数组
T[] getAnnotationsByType(Class<T> annotationClass)

// 返回指定类型的注解,如果没有返回空数组,只包含直接标注的注解,不包含inherited的注解
T getDeclaredAnnotation(Class<T> annotationClass)

// 返回指定类型的注解,如果没有返回空数组,只包含直接标注的注解,不包含inherited的注解
T[] getDeclaredAnnotationsByType

// 返回该元素上的所有注解,如果没有任何注解则返回一个空数组,只包含直接标注的注解,不包含inherited的注解
Annotation[] getDeclaredAnnotations();

通过一个实例再次说明一下注解的使用过程:

定义注解

@Documented
@Target({ElementType.TYPE, ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotaion {
    String getValue() default "this is myAnntaion";
    int order() default 0;
}

使用注解

@MyAnnotaion(getValue = "annotation on class")
public class Demo {

    @MyAnnotaion(getValue = "annotation on filed")
    public String name;

    @MyAnnotaion(getValue = "annotation on method")
    public void hello(){
    }

    @MyAnnotaion
    public void defaultMethod(){

    }
}

利用反射读取注解中的值。

public class TestDemo {
    public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException {
        /**
         * 获取类上的注解
         */
        Class<Demo> demoClass = Demo.class;
        Annotation[] annotaion = demoClass.getAnnotations();
        printAnnotation(annotaion);

        /**
         * 读取成员变量上的注解
         */
        Field name = demoClass.getField("name");
        Annotation[] getOnFiled = name.getAnnotations();
        printAnnotation(getOnFiled);

        /**
         * 读取方法上的注解
         */
        Method hello = demoClass.getMethod("hello", null);
        MyAnnotaion onMethod = hello.getAnnotation(MyAnnotaion.class);
        System.out.println(onMethod.getValue());

        /**
         * 获取默认方法上的注解
         */
        Method defaultMethod = demoClass.getMethod("defaultMethod", null);
        MyAnnotaion onDefaultMethod = defaultMethod.getAnnotation(MyAnnotaion.class);
        System.out.println(onDefaultMethod.getValue());

    }

    public static void printAnnotation(Annotation... annotations) {
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }
    }
}

运行结果

@annocation.MyAnnotaion(getValue=annotation on class, order=0)
@annocation.MyAnnotaion(getValue=annotation on filed, order=0)
annotation on method
this is myAnntaion

本文分享自微信公众号 - JAVA乐园(happyhuangjinjin88)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-11-12

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Sentinel控制台监控数据持久化到MySQL数据库

    根据官方wiki文档,Sentinel控制台的实时监控数据,默认仅存储 5 分钟以内的数据。如需持久化,需要定制实现相关接口。

    java乐园
  • 1、Spring注解之@RequestMapping

    @RequestMapping是一个用来处理请求地址映射的注解,可用于类或者方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。

    java乐园
  • Synchronized 同步方法的八种使用场景

    本文将介绍7种同步方法的访问场景,我们来看看这七种情况下,多线程访问同步方法是否还是线程安全的。这些场景是多线程编程中经常遇到的,而且也是面试时高频被问到的问题...

    java乐园
  • (85) 注解 / 计算机程序的思维逻辑

    上节我们探讨了反射,反射相关的类中都有方法获取注解信息,我们在前面章节中也多次提到过注解,注解到底是什么呢? 在Java中,注解就是给程序添加一些信息,用字符...

    swiftma
  • Java 注解 Annotation

    java404
  • Java中的注解

    Annotation(注解)是Java JDK5及其以后版本中引入的一个特性。注解是Java的一个新的类型(与接口类似),它与类、接口、枚举是在同一个层次,它们...

    卡尔曼和玻尔兹曼谁曼
  • 基础篇:深入解析JAVA注解机制

    在代码里定义的注解,会被jvm利用反射技术生成一个代理类,然后和被注释的代码(类,方法,属性等)关联起来

    潜行前行
  • Java注解Annotation与自定义注解详解

    开发中经常使用到注解,在项目中也偶尔会见到过自定义注解,今天就来探讨一下这个注解是什么鬼,以及注解的应用场景和如何自定义注解。

    yaphetsfang
  • 详解Java中的注解

    在Java中,注解(Annotation)引入始于Java5,用来描述Java代码的元信息,通常情况下注解不会直接影响代码的执行,尽管有些注解可以用来做到影响代...

    技术小黑屋
  • 深入浅出Java注解

    注解对于开发人员来讲既熟悉又陌生,熟悉是因为只要你是做开发,都会用到注解(常见的@Override);陌生是因为即使不使用注解也照常能够进行开发...

    open

扫码关注云+社区

领取腾讯云代金券