网站整体色调,浙江企业年报网上申报入口,长安网页设计公司,邦派巴洛特网站是谁做的呀引言
本博客总结自《Java 编程思想》第 20 章。
一、什么是注解
注解是 Java 5 引入的一种通过反射机制实现的语法特性#xff0c;开发者可以通过在类、域、方法等元素前面标记一个“标签”达到对程序的源码、类信息或运行时进行某种说明或处理的效果#xff0c;尽可能地简…引言
本博客总结自《Java 编程思想》第 20 章。
一、什么是注解
注解是 Java 5 引入的一种通过反射机制实现的语法特性开发者可以通过在类、域、方法等元素前面标记一个“标签”达到对程序的源码、类信息或运行时进行某种说明或处理的效果尽可能地简化代码从而使程序开发更高效。但需要注意的是编译器要确保在其构造路径上必须有对应注解的定义。
Java 中在 1.5 之初内置了三个标准注解Deprecated、Override 、SupressWarning 。我们经常会在程序的各个角落看到它们。
以SupressWarning为例
package java.lang;import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
Retention(RetentionPolicy.SOURCE)
public interface SuppressWarnings {String[] value();
}
它一般用于去除不恰当的编译警告俗称“报黄”。随着Java 慢慢的发展也逐渐引入了更多的注解比如在 Java 8 伴随着 Lambda表达式的加入而一同入住 Java 大家庭的 FunctionalInterface 注解
package java.lang;import java.lang.annotation.*;Documented
Retention(RetentionPolicy.RUNTIME)
Target(ElementType.TYPE)
public interface FunctionalInterface {} 二、如何声明注解
注解的定义非常类似接口不同的是像上面的例子中我们要在 interface 关键字前面加 “”以此来声明这是一个注解。
除此之外Java 提供了四个元注解
Target
Retention
Documented
Inherited
其中Target 、Retention 在定义注解时一般情况下都是必选项。
元注解专职负责注解其他注解。 Target 表示该注解可以用于什么地方。需要给它传入一个ElementType 枚举对象常用选项有 CONSTRUCTOR : 构造器声明 FIELD : 域声明包括enum实例 LOCAL_VARIABLE : 局部变量声明 METHOD : 方法声明 PACKAGE : 包声明 PARAMETER : 参数声明 TYPE : 类、接口包括注解声明、enum 声明 Retention 表示需要在什么级别保存该注解。需要传入一个 RetentionPolicy 可选值 SOURCE : 注解将被编译器丢弃。 CLASS : 注解在class文件中使用但会被 JVM 丢弃。 RUNTIME : vm将会在运行期间也保留该注解因此可以通过反射机制读取注解的信息。 Documented : 将此注解包含在javadoc 中。 Inherited 允许子类继承父类的注解。 注解定义示例
package com.mht.demo.注解;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;Target(ElementType.METHOD)
Retention(RetentionPolicy.RUNTIME)
public interface MyTest {public int id();public String description() default no description;
}
可以看到注解的确非常像接口同时和其他任何 Java 接口一样注解也将会被编译成为一个 class 文件。
MyTest 用到了两个元注解Target 、Retention 如上所示MyTest 只能用于方法上如果希望自己的注解既可以用于方法上也可以用于类上那么可以这样写
Target({ ElementType.METHOD, ElementType.TYPE })
Retention用于定义你的注解应用在什么级别源代码(SOURCE)、类文件(CLASS)、运行时(RUNTIME)。
注解中往往包含一些元素比如上例中的 id 、description 。
在分析和处理注解时程序或工具可以利用这些值它们看起来像接口中的抽象方法唯一不同的是我们可以为这些元素指定默认值。对于没有任何元素的注解如 FunctionalInterface 被称为标记注解。
编译器会对注解中的元素进行类型检查因此将这些元素与数据库相关联是安全的。注解元素可用的类型有一定的限制它不允许任何包装类型。被允许的注解元素类型有 1、8大基本类型 2、String 3、Class 4、enum 5、Annotion : 嵌套注解非常有用的技巧 6、以上类型的数组 如果使用了除上述几种以外的其他类型那么编译器就会报错。 注意注解的元素值永远不能为null要么在声明元素之初设置默认值要么就在使用注解时添加该元素值而且必须是不为 null 的值如果希望注解中的某个元素是必填项那么在声明时就可以不为其指定默认值。因此注解处理器无法通过null 来判断元素是否缺失。为了绕开这个限制一般会通过 自己定义特殊值来判断元素是否存在比如 -1 或 。
default 关键字来定义元素的默认值在使用该注解时如果没有给出元素的值 那么注解处理器就会使用此元素的默认值。
三、注解的使用与自定义注解处理器
以 MyTest 为例我们来看看注解如何使用以及如何处理这个注解。
public class SomeService {MyTest(id 1, description Hello Annotation! Hello 2020 !)public void testMyTestFeature() {System.out.println(这是testMyTestFeature()方法!);}
}
我们定义了一个类声明了一个方法并为其标记我们的 MyTest 注解。
接下来我们来实现一个注解处理器 MyTestProcessor
/*** MyTest注解处理器* author mht**/
public class MyTestProcessor {public static void processMyTest(Class? clz) {Method[] declaredMethods clz.getDeclaredMethods();// getAnnotation() 方法会返回指定类型的注解如果没有则返回nullMyTest myTest declaredMethods[0].getAnnotation(MyTest.class);// 这里一般都会判断获取到的注解是否为空if (myTest ! null) {System.out.println(找到标记注解id: myTest.id() , 描述 myTest.description());}}public static void main(String[] args) {processMyTest(SomeService.class);}
}
执行 main 方法测试输出结果
找到标记注解id:1, 描述Hello Annotation! Hello 2020 !
注解处理器虽然名字听起来很专业但实际上我们并不需要为我们处理注解的类或方法继承或实现什么。正如第一节开始所说的注解是一种通过反射机制来实现的特性我们可以通过 Class 对象来获取我们想要的注解。
像上面的代码有点过于简单了一般情况下我们可能会为一个目标添加多个注解因此一般的处理注解的思路就是 1、获取类信息Class 对象可以直接获取类上的注解对象或注解对象数组 2、通过 getDeclaredMethods() 等方法获取目标信息有时也有可能是 类或域 3、通过 getAnnotation(ClassT annotationClass)、getAnnotations() 等方法获取一个或多个待处理的注解对象。 4、通过注解对象获取内部元素根据其值进行逻辑处理。 这是一个一般的注解处理思路许多框架中的注解处理往往比较复杂且经常需要配合遍历来处理多个类信息多个注解的情况。
值得注意的是注解往往都是被动的处理它不能主动发出某种信号传递给注解处理器也就是说我们必须主动找到这些注解或将携带他们的类传入注解处理器。
某些框架在批量处理注解的时候就必须为注解处理器指定一个尽可能小的路径范围以此来扫描该路径下的类信息。
Mybatis 中的 MapperScan 就是一个很好的例证如果不为其指定一个扫描路径Mybatis 框架就可能必须从 classpath 的根路径找起这会非常影响框架处理效率。
综上就是关于 注解的总结和思考欢迎文末留言。