Java-注解(@Annotation)

一、缘由

    上篇也提到过要开 spring cloud 系列,但是避免不了就是 spring boot,那针对这个我们首先要明白的就是 spring boot 启动到底帮我做了那些事,明白这些我们后面使用起来的时候就会得心应手,但是你会发现一个问题,那就是 spring boot 就是通过这些注解去加载的,所以这个地方我要来聊聊注解,当然 spring 也好多都是注解去实现的,这样我们就要更改明白注解到底是帮我们做了哪些事。

二、什么是注解以及注解的作用(Annotation):

    注解是插入到代码中的元数据,JDK5.0 以后的版本引入。注解必须有编译器或者虚拟机来解析它,才能发挥自己的作用,它可以生成文件,可以执行编译时进行测试和验证格式等等。因为本质上,注解是一种特殊的接口,程序可以通过反射来获取指定程序元素的注解对象,然后通过注解对象来获取注解里面的元数据。

    注解的作用:

    1. 编写文档:通过代码里标识的元数据生成文档;

    2. 代码分析:通过代码里标识的元数据对代码进行分析;

    3. 编译检查:通过代码里标识的元数据让编译器能实现基本的编译检查;

二、注解的类型

    1. 内置注解

    @Override: 表示当前的方法可以覆盖父类中的方法;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
View Code

    @Deprecated:用于标注该方法已经过时,当程序员调用的时候,编译器会发生警告的信息;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
View Code

   @SuppressWarnnings: 用于关闭不当的编译器警告;

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {String[] value();}
View Code

   内部有个 string 数组,简单的说一下改数组接收的几个常用的几个参数:

   1.deprecation:使用了由 @Deprecated 修饰的类或方法时的警告;

   2.unchecked:执行了未检查的转换时的警告;

   3.path:在类路径、源文件路径等中有不存在的路径时的警告;

   4.all: 所有情况的警告;

   5. 剩下的参考 http://www.cnblogs.com/fsjohnhuang/p/4040785.html

   接下来简单的介绍下上面 3 种使用的方法:

public class Person {
@Deprecated
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> getCountryName(){
    System.out.printf(</span>"我是地球人"<span style="color: rgba(0, 0, 0, 1)">);
}

}

public class ChinaPerson extends Person {
@SuppressWarnings(
"deprecation")
@Override
public void getCountryName(){
System.out.printf(
"我是中国人");
}

}
public class Main {

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> main(String[] args) {
    ChinaPerson chinaPerson</span>=<span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ChinaPerson();
    chinaPerson.getCountryName();
}

}

View Code

   2. 元注解

   @Target: 用来约束注解可以应用到的地方:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    /**
     * Returns an array of the kinds of elements an annotation type
     * can be applied to.
     * @return an array of the kinds of elements an annotation type
     * can be applied to
     */
    ElementType[] value();
}
View Code

   该注解可以接收一个 ElementType 数组,该类型是个枚举类型可以接收如下参数:

   1.PACKAGE:标明注解可以用于包声明;

   2.CONSTRUCTOR:标明注解可以用于构造函数声明;

   3.PARAMETER:标明该注解可以用于参数声明;

   4.METHOD:标明该注解可以用于方法声明;

   5.FIELD:标明该注解可以用于字段 (域) 声明,包括 enum 实例;

   6.TYPE:标明该注解可以用于类、接口(包括注解类型)或 enum 声明;

   7.LOCAL_VARIABLE:标明注解可以用于局部变量声明;

   8.ANNOTATION_TYPE:标明注解可以用于注解声明 (应用于另一个注解上);

   9.TYPE_PARAMETER:标明注解可以用于类型参数声明(1.8 新加入);

   10.TYPE_USE:类型使用声明(1.8 新加入 );

   @Retention:用来约束注解的生命周期:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();}
View Code

   可接收的参数如下:

   1.SOURCE: 在源文件中有效, 被编译器丢弃;

   2.CLASS: 在 class 文件中有效,会被 VM 丢弃,当没有定义 CLASS 默认 CLASS;

   3.RUNTIME: 在运行时有效, 可以通过反射机制读取注解的信息;

   @Documented:被修饰的注解会生成到 javadoc 中

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}
View Code

   @Inherited: 允许子类继承父类的注解,即它所标注的 Annotation 将具有继承性, 通过使用 @Inherited,可以让子类 Class 对象使用 getAnnotations() 获取父类被 @Inherited 修饰的注解

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}
View Code

   3. 自定义注解

   使用 @interface 自定义注解,自动继承了 java.lang.annotation.Annotation 接口。在定义注解时,不能继承其他的注解或接口。@interface 用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型。可以通过 default 来声明参数的默认值。

   接下来我们做个简单的案例来体验下:

import java.lang.annotation.*;

/**

  • Created by wangt on 2018/6/17.
    */
    @Inherited
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Table {
    String value()
    default "";
    }

import java.lang.annotation.*;

/**

  • Created by wangt on 2018/6/17.
    */
    @Inherited
    @Target({ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Column {
    String value()
    default "";
    }

/**

  • Created by wangt on 2018/6/17.
    */
    @Table(
    "sutdent")
    public class StudentDto {

    @Column("id")
    private String id;

    @Column("username")
    private String name;

    public StudentDto(String id, String name) {
    super();
    this.id = id;
    this.name = name;
    }

    public String getId() {
    return id;
    }

    public void setId(String id) {
    this.id = id;
    }

    public String getName() {
    return name;
    }

    public void setName(String name) {
    this.name = name;
    }

}

import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**

  • Created by wangt on 2018/6/17.
    */
    public class Test {
    public static void main(String[] args){
    StudentDto studentDto
    =new StudentDto("1","wtz");
    StudentDto studentDto2
    =new StudentDto("2","www");
    String sql
    = assembleSqlFromObj(studentDto);
    String sql1
    = assembleSqlFromObj(studentDto2);
    System.out.println(sql);
    System.out.println(sql1);
    }

    private static String assembleSqlFromObj(Object object) {
    Table table
    = object.getClass().getAnnotation(Table.class);
    StringBuffer sbSql
    = new StringBuffer();
    String tableName
    = table.value();
    sbSql.append(
    "select * from" + tableName + "where 1=1");
    Field[] fileds
    = object.getClass().getDeclaredFields();
    for (Field f : fileds) {
    String fieldName
    = f.getName();
    String methodName
    = "get" + fieldName.substring(0, 1).toUpperCase()
    + fieldName.substring(1);
    try {
    Column column
    = f.getAnnotation(Column.class);
    if (column != null) {
    Method method
    = object.getClass().getMethod(methodName);
    String value
    = (String) method.invoke(object);
    if (value != null && !value.equals("")) {
    if (!isNum(column.value()) && !isNum(value)) {
    // 判断参数是不是 in 类型参数 1,2,3
    if (value.contains(",")) {
    sbSql.append(
    "and" + column.value()+ "in (" + value + ")");
    }
    else {
    sbSql.append(
    "and" + column.value() + "like'%"+ value +"%' ");
    }
    }
    else {
    sbSql.append(
    "and" + column.value() + "=" + value + " ");
    }
    }
    }
    }
    catch (Exception e) {
    e.printStackTrace();
    }
    }
    return sbSql.toString();
    }

    public static boolean isNum(String target) {
    boolean isNum = false;
    if (target.toLowerCase().contains("id")) {
    isNum
    = true;
    }
    if (target.matches("\d+")) {
    isNum
    = true;
    }
    return isNum;
    }

}

View Code

  这个自己想想是啥原理,,哈哈不懂问我。

四、总结

 Java 的注解原理是 Java 的反射机制,要是自定义注解那块没有看懂的话,再去读读反射把,这是一个很重要的东西,有机会我也会写一篇反射方面的;

 欢迎大家加群:438836709,有问题可以咨询我

 欢迎大家关注我微信公众号: