点击上方 Java后端,选择设为星标

作者 | 工匠初心
链接 | blog.csdn.net/fengdongsuixin
我们在平时的开发过程中看到很多如@Override,@SuppressWarnings,@Test等样式的代码就是注解,注解是放到类、构造器、方法、属性、参数前的标记。
二. Annotation的作用
给某个类、方法…添加了一个注解,这个环节仅仅是做了一个标记,对代码本身并不会造成任何影响,需要后续环节的配合,需要其他方法对该注解赋予业务逻辑处理。就如同我们在微信上发了一个共享定位,此时并没有什么用,只有当后面其他人都进入了这个共享定位,大家之间的距离才能明确,才知道该怎么聚在一起。
注解分为三类:
2.1 编译器使用到的注解
如@Override,@SuppressWarnings都是编译器使用到的注解,作用是告诉编译器一些事情,而不会进入编译后的.class文件。
@Override:告诉编译器检查一下是否重写了父类的方法;
@SuppressWarnings:告诉编译器忽略该段代码产生的警告;
对于开发人员来说,都是直接使用,无需进行其他操作
2.2 .class文件使用到的注解
需要通过工具对.class字节码文件进行修改的一些注解,某些工具会在类加载的时候,动态修改用某注解标注的.class文件,从而实现一些特殊的功能,一次性处理完成后,并不会存在于内存中,都是非常底层的工具库、框架会使用,对于开发人员来说,一般不会涉及到。
2.3 运行期读取的注解
一直存在于JVM中,在运行期间可以读取的注解,也是最常用的注解,如Spring的@Controller,@Service,@Repository,@AutoWired,Mybatis的@Mapper,Junit的@Test等,这类注解很多都是工具框架自定义在运行期间发挥特殊作用的注解,一般开发人员也可以自定义这类注解。微信搜索 web_resource 关注获取更多推送
三. 定义Annotation
我们使用@interface来定义一个注解
public @interface Table {String value() default "";}
public Colum {String value() default "";String name() default "";String dictType() default "";}
@Table注解仅用于类上,
@Colum注解仅用于属性上,怎么办?而且开始提到的三类注解,一般开发人员用的都是运行期的注解,那我们定义的是吗?
3.1 元注解
3.1.1 @Target
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();}
public enum ElementType {/** 通过ElementType.TYPE可以修饰类、接口、枚举 */TYPE,/** 通过ElementType.FIELD可以修饰类属性 */FIELD,/** 通过ElementType.METHOD可以修饰方法 */METHOD,/** 通过ElementType.PARAMETER可以修饰参数(如构造器或者方法中的) */PARAMETER,/** 通过ElementType.CONSTRUCTOR可以修改构造器 */CONSTRUCTOR,/** 通过ElementType.LOCAL_VARIABLE可以修饰方法内部的局部变量 */LOCAL_VARIABLE,/** 通过ElementType.ANNOTATION_TYPE可以修饰注解 */ANNOTATION_TYPE,/** 通过ElementType.PACKAGE可以修饰包 */PACKAGE,/*** 可以用在Type的声明式前** @since 1.8*/TYPE_PARAMETER,/*** 可以用在所有使用Type的地方(如泛型、类型转换等)** @since 1.8*/TYPE_USE}
(ElementType.PACKAGE)public Table {String value() default "";}

@Tablepackage annotation;class PackageInfo {public void hello() {System.out.println("hello");}}

(ElementType.TYPE_USE)public NoneEmpty {String value() default "";}
(ElementType.TYPE_PARAMETER)public NoneBlank {String value() default "";}
很明显使用ElementType.TYPE_PARMETER修饰的注解@NoneBlank无法在泛型使用的时候编译通过,仅能用于类的泛型声明,而通过ElementType.TYPE_USE修饰的注解@NoneEmpty可以。微信搜索 web_resource 关注获取更多推送 
3.1.2 @Retention
可以用于定义注解的生命周期,参数为枚举RetentionPolicy,包括了SOURCE,CLASS,RUNTIME 
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.ANNOTATION_TYPE)public Retention {/*** Returns the retention policy.* @return the retention policy*/RetentionPolicy value();}public enum RetentionPolicy {/*** 仅存在于源代码中,编译阶段会被丢弃,不会包含于class字节码文件中.*/SOURCE,/*** 【默认策略】,在class字节码文件中存在,在类加载的时被丢弃,运行时无法获取到*/CLASS,/*** 始终不会丢弃,可以使用反射获得该注解的信息。自定义的注解最常用的使用方式。*/RUNTIME}
3.1.3 @Documented
表示是否将此注解的相关信息添加到javadoc文档中 
3.1.4 @Inherited
定义该注解和子类的关系,使用此注解声明出来的自定义注解,在使用在类上面时,子类会自动继承此注解,否则,子类不会继承此注解。注意,使用@Inherited声明出来的注解,只有在类上使用时才会有效,对方法,属性等其他无效。 
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Inheritedpublic Person {String value() default "man";}
@Personpublic class Parent {}//子类也拥有@Person注解class Son extends Parent {}
3.2 定义注解小结
用@interface定义注解 
可以添加多个参数,核心参数按约定用value,为每个参数可以设置默认值,参数类型包括基本类型、String和枚举 
可以使用元注解来修饰注解,元注解包括多个,必须设置 
@Target
和
@Retention
,
@Retention
一般设置为
RUNTIME
。
四. Annotation处理
我们前面已经提到光配置了注解,其实没有作用,需要通过相应的代码来实现该注解想要表达的逻辑。 
注解定义后也是一种class,所有的注解都继承自 
java.lang.annotation.Annotation
,因此,读取注解,需要使用反射API。
//定义的注解(ElementType.FIELD)(RetentionPolicy.RUNTIME)public Colum {String value() default "";//用于表示某个属性代表的中文含义String name() default "";}用注解@Colum来修饰某个类的属性 
public class Person {(name = "姓名")private String name;(name = "性别")private String gender;(name = "年龄")private int age;(name = "住址")private String address;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getGender() {return gender;}public void setGender(String gender) {this.gender = gender;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}}通过反射读取这个类的所有字段的中文含义,并保存到list中,然后打印出来 
public static void main(String[] args) throws ClassNotFoundException {List<String> columNames = new ArrayList<>();Class clazz = Class.forName("annotation.Person");//获取Person类所有属性Field[] fields = clazz.getDeclaredFields();for (Field field : fields){//获取该属性的Colum注解Colum colum = field.getAnnotation(Colum.class);//或者可以先判断有无该注解field.isAnnotationPresent(Colum.class);//将该属性通过注解配置好的中文含义取出来放到集合中columNames.add(colum.name());}//打印集合columNames.forEach((columName) -> System.out.println(columName));}结果如下: 
姓名性别年龄住址比如我们有一些常见的应用场景,需要把网站上的列表导出成excel表格,我们通过注解的方式把列名配置好,再通过反射读取实体需要导出(是否需要导出,也可通过注解配置)的每个字段的值,从而实现excel导出的组件。 
五. 总结
本文只是抛砖引玉地讲解了注解的基本概念,注解的作用,几种元注解的功用以及使用方法,并通过一个简单的例子讲解了一下注解的处理,并不全面,文中通过Field讲解了注解的基本Api,但注解还可以修饰类、构造器、方法等,也有相对应的注解处理方法,大家可自行查一下API手册相关内容,大同小异,有不对之处,请批评指正,望共同进步,谢谢! 
-END- 
如果看到这里,说明你喜欢这篇文章,请转发、点赞。微信搜索「web_resource」,关注后回复「进群」或者扫描下方二维码即可进入无广告交流群。 
↓扫描二维码进群↓ 
推 
荐
阅
读
1. 从零搭建创业公司后台技术栈 
2. 
如何阅读 Java 源码?
3. 某小公司RESTful、前后端分离的实践 
4. 该如何弥补 GitHub 功能缺陷? 
5. 团队开发中 Git 最佳实践
喜欢文章,点个 
在看
本文分享自微信公众号 - Java后端(web_resource)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。



