成都平台网站建设公司,邯郸网络科技公司,优秀电子商务网站,网页游戏排行榜奇迹1. 注解的简介
从JDK 5开始#xff0c;Java增加了对元数据#xff08;MetaData#xff09;的支持#xff0c;也就是注解#xff08;Annotation#xff09;。
注解就是代码里的特殊标记#xff0c;这些标记可以在编译、类加载、运行时被读取#xff0c;并执行相应的处…1. 注解的简介
从JDK 5开始Java增加了对元数据MetaData的支持也就是注解Annotation。
注解就是代码里的特殊标记这些标记可以在编译、类加载、运行时被读取并执行相应的处理。
注解提供了一种为程序元素包、类、构造器、方法、成员变量等设置元数据的方法从某些方面来看注解就像修饰符一样可用于修饰包、类、构造器、方法、成员变量、参数、局部变量。
通过使用注解开发人员可以在不改变原有逻辑的情况下在源文件中嵌入一些补充信息代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证或进行部署。
值得指出的是如果希望让程序中的注解在编译时起作用只有通过某种配套的工具对注解中的信息进行访问和处理。访问和处理注解的工具统称为APT即Annotation Processing Tool。可以将此APT注解处理工具理解成一套用于获取并处理注解的程序代码。没有使用APT注解处理工具处理过的注解在程序中是不会起任何作用的。
另外注解除了用来描述注解这一概念之外它还表示一个接口。Java代码中通过interface定义的注解类型都默认实现了Annotation接口通过反射获取的注解就是一个Annotation对象。
package java.lang.annotation;
public interface Annotation {// 返回注解对象的实际类型即是哪个Annotation接口的实现类Class? extends Annotation annotationType();
}在Java代码中对于可以接受注解的程序元素需要实现AnnotatedElement接口。如Class(类)、Constructor(构造器)、Field(成员变量)、Method(成员方法)、Package(包)它们都实现了AnnotatedElement接口。
2. 自定义注解
2.1 语法
//定义注解类型元注解
...
元注解
public interface 注解名称 {//定义成员变量成员变量类型 成员变量名() [default 默认值]成员变量类型 成员变量名() [default 默认值]...
}使用interface关键字定义注解类型 定义注解类型时还可以通过一个或多个元注解进行修饰 注解类型可以定义成员变量也可以不定义成员变量 成员变量的类型只能是8种基本数据类型、String、Class、注解类型、枚举类型、以及这些类型的一维数组类型 成员变量可以通过default关键字设置默认值设置了默认值的成员变量在使用注解时可以省略不赋值。
注意 如果定义注解时定义了一个名为value的成员变量并且使用此注解时只需要为此value成员变量赋值即
1. 只定义了成员变量value
2. 除了value之外的其他成员变量在定义时都设置了默认值如上两种情况下则使用该注解时可以直接在该注解后的圆括号里指定成员变量value的值无需使用value变量值的形式。 此时若value表示的是一个数组类型并且指定值时只需要指定一个元素值则可以不用加花括号。 2.2 自定义注解示例
//1. 定义无成员变量的注解类型
public interface Annotation0 {
}//2. 定义有成员变量的注解类型
public interface Annotation1 {String name();int age(); String[] children();Class[] clazzes();
}//3. 定义有成员变量的注解类型并给部分成员变量设置了默认值
public interface Annotation2 {String name();int age();String city() default shenzhen;
}//4. 定义存在一个value成员变量的注解。
//因为成员变量name设置了默认值所有使用此注解时可以只给value成员变量赋值
public interface Annotation3 {String[] value();String name() default 19;
}//5. 定义被多个元注解修饰的注解类型
Documented //使用此元注解修饰的注解Annotation4将被javadoc工具提取成文档
Target({ElementType.FIELD,ElementType.METHOD}) //使用此元注解修饰Annotation4后Annotation4注解只能用来修饰指定的程序元素成员变量和方法。
Retention(RetentionPolicy.RUNTIME) //使用此元注解修饰的Annotation4可以保留到运行时。
public interface Annotation4 {String name();
}2.3 使用注解示例
通过 注解类型名 赋值列表(可省略)的方法使用注解
Annotation0
public void method(){
}Annotation1(namezengk, age26, children{aa, bb}, clazzes{Object.class, TestAnnotation1.class})
public void method(){
}//没有对存在默认值的city成员变量赋值
Annotation2(namezengk, age26)
public void method(){
}//Annotation3(value{xx,yy})
Annotation3({xx,yyy})
public void method(){
}//Annotation3(value{yy}) 此方式也可以
//Annotation3(valueyy) 此方式也可以
//Annotation3({yy}) 此方式也可以
Annotation3(yy)
public void method(){
}3. 标记注解元数据注解
根据注解在定义时是否有成员变量可以把注解分为两类
1. 标记注解 没有定义成员变量的Annotation类型被称为标记。这种注解仅利用自身的存在与否来提供信息。如Override
2. 元数据注解 定义了成员变量的Annotation类型因为他们可以接受更多的元数据所以也被称为元数据注解。
3.1 元注解
元注解Meta Annotation就是用来修饰注解的注解
3.1.1 Retention
package java.lang.annotation;Documented
Retention(RetentionPolicy.RUNTIME)
Target(ElementType.ANNOTATION_TYPE)
public interface Retention {RetentionPolicy value();
}Retention只能用于修饰Annotation类型用于指定被修饰的Annotation可以保留多长时间。Retention元注解只定义了一个RetentionPolicy枚举类型的value成员变量value成员变量的值只有如下三个
RetentionPolicy.CLASS编译器把注解信息记录在class文件中但不会保留到运行时期。因此运行时不可获取注解信息。这是默认值。RetentionPolicy.RUNTIME编译器把注解信息记录在class文件中且会保留到运行时期。因此运行时可以通过反射获取注解信息。RetentionPolicy.SOURCE注解信息只保留在源代码中编译器直接丢弃这种注解信息。如果需要通过反射获取某个注解的元数据则必须使用Retention(RetentionPolicy.RUNTIME)来修饰此注解类型。 3.1.2 Target
package java.lang.annotation;Documented
Retention(RetentionPolicy.RUNTIME)
Target(ElementType.ANNOTATION_TYPE)
public interface Target {ElementType[] value();
}Target只能用于修饰Annotation类型用于指定被修饰的Annotation能用于修饰哪些程序元素。Target元注解只定义了一个ElementType[]数组类型的value成员变量其中ElementType是枚举类型value成员变量的值可以是如下几个
ElementType.TYPE 指定该策略的Annotation只能修饰类接口包括注解类型枚举定义。
ElementType.FIELD 指定该策略的Annotation只能修饰成员变量。
ElementType.METHOD 指定该策略的Annotation只能修饰方法定义。
ElementType.PARAMETER 指定该策略的Annotation只能修饰参数。
ElementType.CONSTRUCTOR 指定该策略的Annotation只能修饰构造方法。
ElementType.LOCAL_VARIABLE 指定该策略的Annotation只能修饰局部变量。
ElementType.ANNOTATION_TYPE 指定该策略的Annotation只能修饰Annotation。
ElementType.PACKAGE 指定该策略的Annotation只能修饰包定义。3.1.3 Documented
package java.lang.annotation;Documented
Retention(RetentionPolicy.RUNTIME)
Target(ElementType.ANNOTATION_TYPE)
public interface Documented {
}Documented用于指定被该元注解修饰的Annotation类将被javadoc工具提取成文档如果定义Annotation类时使用了Documented修饰则所有使用该Annotation修饰的程序元素的API文档中将会包含该Annotation说明
3.1.4 Inherited
package java.lang.annotation;Documented
Retention(RetentionPolicy.RUNTIME)
Target(ElementType.ANNOTATION_TYPE)
public interface Inherited {
}Inheritd用于指定被它修饰的Annotation将具有继承性。如果定义Annotation类时使用了Inherited修饰那么当使用该Annotation修饰了某个类则这个类的子类也将自动被此Annotation修饰。
4. Java中提供的基本Annotation介绍
4.1 Override
package java.lang;import java.lang.annotation.*;Target(ElementType.METHOD)
Retention(RetentionPolicy.SOURCE)
public interface Override {
}Override用来修饰方法强制类中的方法必须是重写了父类中的方法。当一个类中的某个方法使用Override修饰后则必须保证此类的父类或父接口中定义了一个被该方法重写的方法否则会编译报错。使用Override可以避免重写方法时的笔误。
4.2 Deprecated
package java.lang;import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;Documented
Retention(RetentionPolicy.RUNTIME)
Target(value{CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public interface Deprecated {
}Deprecated用于表示某个程序元素已过时。当程序元素如成员变量方法等被Deprecated修饰后的调用这些程序元素时编译器将会给出警告。
4.3 SuppressWarnings
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();
}SuppressWarnings修饰某个程序元素后对于作用在此程序元素上的SuppressWarning指定的编译器警告会被取消掉。如果某个程序元素被SuppressWarnings修饰那么该程序元素范围内的其他程序元素也会被此SuppressWarnings修饰。例如当一个类被SuppressWarnings修饰此类中的某个方法又被另一个SuppressWarnings修饰那么对于这两个SuppressWarnings指定的所有编译器警告作用在此方法上时都会被取消。
5. 提取注解信息
对于用Retention(RetentionPolicy.RUNTIME)修饰的运行时注解可以通过反射提取Annotation中的元数据。由于是运行时才处理注解信息并且使用了大量的反射技术所以效率会比较慢。
对于用Retention(RetentionPolicy.CLASS)或Retention(RetentionPolicy.SOURCE)修饰的注解需要在编译时处理此时不能使用反射技术需通过javax.annotation.processing包下的Processor接口来实现一个注解处理器Annotation Processor一般采用继承AbstractProcessor的方式定义一个注解处理器。通过使用注解处理器来处理和提取Annotation信息。
5.1 提取运行时注解信息
上文已经提到过在Java中通过Annotation对象表示一个注解。所以只有得到注解的Annotation对象才能提取此注解中的元数据。
那么怎么得到Annotation对象呢上文也有提到对于可以接受注解的程序元素需要实现AnnotatedElement接口而其中的AnnotationElement接口就提供了获取Annotation对象的方法。所以只需要得到AnnotatedElement的实现类对象就能获取到Annotation对象从而提取Annotation中的元数据。
而AnnotatedElement的实现类在上文中也有提到就是Class、Method、Field、Constructor等这些程序元素在Java中对应的类。于是我们可以通过反射获取到Class、Constructor、Method、Field这些类的对象。
通过反射提取Annotation中的元数据的步骤总结如下
1. 通过反射得到Class、Constructor、Method、Field这些实现了AnnotationElement接口的类的对象。2. 通过AnnotationElement实现类对象调AnnotationElement提供的API获取注解对应的Annotation对象。3. 通过Annotation对象获取到注解的元数据。5.1.1 AnnotationElement接口的API
/**
* 判断该程序元素上是否存在指定注解类型的注解存在返回true否则返回false。
*/
boolean isAnnotationPresent(Class? extends Annotation annotationClass);/**
* 返回此程序元素上存在的、指定注解类型的Annotation对象。如果该注解类型的注解不存在则返回null。
*/
T extends Annotation T getAnnotation(ClassT annotationClass);/**
* 返回该程序元素上存在的所有注解
*/
Annotation[] getAnnotations();/**
* 返回直接修饰该程序元素的所有注解
*/
Annotation[] getDeclaredAnnotations();5.1.2 代码示例
通过反射处理运行时注解实现Android中Button按键的点击事件绑定
1. 定义注解类型
Target(ElementType.FIELD)
Retention(RetentionPolicy.RUNTIME)
public interface ButtonAnnotation {int resId();Class? extends View.OnClickListener listenClazz();
}2. 定义注解处理工具
public class ButtonAnnotationProcessTools {public static void process(Object obj){//获取使用了注解的程序元素所在的类的class对象Class clazz obj.getClass();//因为ButtonAnnotation只能修饰成员变量//所以只需变量此类中的成员变量找到被ButtonAnnotation修饰的成员变量Field[] declaredFields clazz.getDeclaredFields();for (Field field : declaredFields) {//因为后面需要给ButtonAnnotation修饰的成员变量赋值//所以对于私有的成员变量需获取访问权限field.setAccessible(true);//判断成员变量是否被ButtonAnnotation修饰boolean annotationPresent field.isAnnotationPresent(ButtonAnnotation.class);if (annotationPresent) {//获取修饰此成员变量的ButtonAnnotation注解对象ButtonAnnotation buttonAnnotation field.getAnnotation(ButtonAnnotation.class);try {Class fieldType field.getType();//对Button类型的成员变量通过注解ButtonAnnotation中的元数据对其进行赋值和设置点击事件监听if (fieldType.equals(Button.class) obj instanceof MainActivity){Activity activity (Activity) obj;//通过ButtonAnnotation中的资源ID得到view对象View view activity.findViewById(buttonAnnotation.resId());//通过ButtonAnnotation中的监听器Class对象利用反射得到View.OnClickListener对象Class? extends View.OnClickListener listenClazz buttonAnnotation.listenClazz();View.OnClickListener onClickListener listenClazz.newInstance();view.setOnClickListener(onClickListener);//给ButtonAnnotation修饰的成员变量赋值field.set(obj, view);}} catch (Exception e) {e.printStackTrace();}}}}
}3. 使用注解对Button类型的成员变量进行赋值并绑定点击事件监听器
public class MainActivity extends AppCompatActivity{ButtonAnnotation(resId R.id.btn, listenClazz ButtonOnClickListener.class)private Button mBtn;Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//使用注解处理工具对ButtonAnnotation进行处理ButtonAnnotationProcessTools.process(this);}class ButtonOnClickListener implements View.OnClickListener{Overridepublic void onClick(View v) {Log.d(TAG, onclick);}}
}5.2 提取编译时注解信息
step1. 继承AbstractProcessor类处理注解
AbstractProcessor实现了Processor接口继承AbstractProcessor重写process方法处理注解信息
/**
* 在 javax.annotation.processing 包下提供了 AbstractProcessor抽象类
*/
public abstract class AbstractProcessor implements Processor/**
* 重写此方法。返回此注解处理器APT 处理的注解类型
* 返回Set集合包含要处理的注解类型String表示注解类名最好使用全路径名
* Java1.7 以后也可以不重写此方法通过注解SupportedAnnotationTypes指定处理的注解类型
* SupportedAnnotationTypes的目标程序元素是类即在自定义的AbstractProcessor子类上使用
*/
public SetString getSupportedAnnotationTypes()/**
* 重写此方法返回处理注解时采用的Java版本
* Java1.7以后也可以不重写此方法通过注解SupportedSourceVersion指定Java版本
* SupportedAnnotationTypes也是作用在自定义的AbstractProcessor子类上
*/
public SourceVersion getSupportedSourceVersion()/**
* 通常不需要重写此方法
*/
public synchronized void init(ProcessingEnvironment processingEnv)/**
* 重写此方法处理注解
*/
public abstract boolean process(Set? extends TypeElement annotation, RoundEnvironment roundEnv);5.2.1 相关API
javax.annotation.processing.RoundEnvironment
public interface RoundEnvironment/**
* 获取被注解a修饰的Element集合此处返回的Element表示的程序元素是类
*/
Set? extends Element getElementsAnnotatedWith(Class? extends Annotation a);javax.lang.model.element.Element
Element表示程序元素可以是包、类、构造器、方法、成员变量等
public interface Element extends javax.lang.model.AnnotatedConstruct/**
* 获取此Element表示的程序元素的名字如当表示类时返回的是类名
*/
Name getSimpleName();/**
* 获取修饰此程序元素的注解参数annotationType指定要获取的注解类型
*/
A extends Annotation A getAnnotation(ClassA annotationType);/**
* 返回此Element所表示的程度元素的类型如包、类、构造器、方法、成员变量等
*/
ElementKind getKind();/**
* 返回此程序元素范围内包含的程序元素如类中包含有构造器方法成员变量
*/
List? extends Element getEnclosedElements();javax.lang.model.element.ElementKind
public enum ElementKind {PACKAGE,/*--------------Declared types------------*/ENUM, //An enum typeCLASS, //A class not described by a more specific kind (like ENUM)ANNOTATION_TYPE, //An annotation typeINTERFACE, //An interface not described by a more specific kind (like ANNOTATION_TYPE)/*-------------Declared Variables--------------*/ENUM_CONSTANT, //An enum constantFIELD, //A field not described by a more specific kind (like ENUM_CONSTANT)PARAMETER, //A parameter of a method or constructorLOCAL_VARIABLE, //A local variableEXCEPTION_PARAMETER, //A parameter of an exception handler/*------------Declared Executables--------------*/METHOD, //A methodCONSTRUCTOR, //A constructorSTATIC_INIT, //A static initializerINSTANCE_INIT, //An instance initializer/** A type parameter. */TYPE_PARAMETER, //A type parameterOTHER, //An implementation-reserved element. This is not the element you are looking for.RESOURCE_VARIABLE; //A resource variable
}