java根据自定义注解对类属性进行排序 java实现自定义注解_java根据自定义注解对类属性进行排序

注解:

注解为我们在代码中添加信息提供一种形式化的方法,使我们可以在源码、编译时、运行时非常方便的使用这些数据。

注解是在JAVA SE5中引入的,注解让代码更干净易读并且可以实现编译期类型检查等。当创建描述性质的类或接口时,如果有重复性的工作,就可以考虑使用注解来简化或自动化该过程。我们可以让注解保存在源代码中,并且利用Annotation API处理注解,得到我们想要的数据并加以处理,注解的使用比较简单,JAVA SE5内置了3种:

  • @Override 表示当前类中的方法将覆盖父类中的方法,如果不写也不会有错,但是@Override可以起到检查作用,如方法名拼写错误,编译器就会报警告信息。
  • @Deprecated 表示被标注的方法已经被废弃了,如果使用编译器会发出警告信息。
  • @SuppressWarnings 关闭不当的编译器警告信息。除非你确定编译器的警告信息是错误的,否则最好不要使用这个注解。

定义注解

先来看内置注解@Override是怎么被定义的,它位于package java.lang之下:



@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}



@Target、@Retention称为元注解:元注解负责注解其他的注释,如:

@Target定义声明的注解的作用域(作用在类上还是方法上)

@Retention定义注解在哪个级别可用,在源代码中(SOURCE)、类文件中(CLASS)、还是运行时(RUNTIME)。

除了@Target、@Retention还有@Documented及@Inherited,下面用一个表格来分别列出他们各自的作用:

java根据自定义注解对类属性进行排序 java实现自定义注解_java_02

@Retention作用范围如下图所示:

java根据自定义注解对类属性进行排序 java实现自定义注解_Java_03

注解处理器

首先来自定义一个注解:



@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationInfo {

 String[] value();

 int requestCode() default 0;
}



  • 注解中定义的方法没有参数,且返回类型仅限于原始类型,字符串,枚举,注解或以上类型的集合
  • 注解中定义的方法可以有默认值

运行时解析注解


@Target(ElementType.METHOD)指明了我们的注解是作用在方法上的

@Retention(RetentionPolicy.RUNTIME)表示注解在程序运行时期也会存在,即注解信息也会加载到虚拟机VM中,所以可以通过反射来获取注解的相关信息:



public class AnnotationExample {

 /**
 * 注解模拟请求权限
 */
 @AnnotationInfo(value = {"android.permission.CALL_PHONE", "android.permission.CAMERA"}, requestCode = 10)
 public void requestPermission() {
 //其他逻辑
 }
}



接着来编写一个运行时解析注解的Java类:AnnotationRuntimeProcessor.java



public class AnnotationRuntimeProcessor {

 public static void main(String[] args) {
 try {
 //获取AnnotationExample的Class对象
 Class<?> cls = Class.forName("com.javastudy.Annotation.AnnotationExample");
 //获取AnnotationExample类中的方法
 Method[] methods = cls.getDeclaredMethods();
 for (Method method : methods) {
 //过滤不含自定义注解AnnotationInfo的方法
 boolean isHasAnnotation = method.isAnnotationPresent(AnnotationInfo.class);
 if (isHasAnnotation) {
 method.setAccessible(true);
 //获取方法上的注解
 AnnotationInfo aInfo = method.getAnnotation(AnnotationInfo.class);
 if (aInfo == null) return;
 //解析注解上对应的信息
 String[] permissions = aInfo.value();
 System.out.println("value: " + Arrays.toString(permissions));

 int requestCode = aInfo.requestCode();
 System.out.println("requestCode: " + requestCode);
 }
 }
 } catch (Exception e) {
 e.printStackTrace();
 }
 }
}



上面的逻辑很简单,反射拿到有注解对应类的Class对象,筛选含有注解的方法,最后获取方法上的注解并解析,运行结果如下:



value: [android.permission.CALL_PHONE, android.permission.CAMERA]
requestCode: 10




编译时解析注解


AbstractProcessor是javax下的API,java和javax都是Java的API(Application Programming Interface)包,java是核心包,javax的x是extension的意思,也就是扩展包。一般继承AbstractProcessor需要实现下面的几个方法:



public class ProcessorExample extends AbstractProcessor {

 @Override
 public synchronized void init(ProcessingEnvironment processingEnvironment) {
 //processingEnvironment提供各种工具类 如Elements Filer Types SourceVersion等
 super.init(processingEnvironment);
 }

 /**
 * 扫描 评估和处理注解代码 生成Java代码
 *
 * @param set 注解类型
 * @param roundEnvironment 有关当前和以前的信息环境 查询出包含特定注解的被注解元素
 * @return 返回true 表示注解已声明 后续Processor不会再处理 false表示后续Processor会处理他们
 */
 @Override
 public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
 return false;
 }

 @Override
 public SourceVersion getSupportedSourceVersion() {
 return super.getSupportedSourceVersion();
 }

 @Override
 public Set<String> getSupportedAnnotationTypes() {
 return super.getSupportedAnnotationTypes();
 }
}



  • init(ProcessingEnvironment env): 每一个注解处理器类都必须有一个空的构造函数。然而,这里有一个特殊的init()方法,它会被注解处理工具调用,并输入ProcessingEnviroment参数。ProcessingEnviroment提供很多有用的工具类Elements, Types和Filer。后面我们将看到详细的内容。
  • process(Set (? extends TypeElement) annotations, RoundEnvironment env): 这相当于每个处理器的主函数main()。你在这里写你的扫描、评估和处理注解的代码,以及生成Java文件。输入参数RoundEnviroment,可以让你查询出包含特定注解的被注解元素。后面我们将看到详细的内容。
  • getSupportedAnnotationTypes(): 这里你必须指定,这个注解处理器是注册给哪个注解的。注意,它的返回值是一个字符串的集合,包含本处理器想要处理的注解类型的合法全称。换句话说,你在这里定义你的注解处理器注册到哪些注解上。
  • getSupportedSourceVersion(): 用来指定你使用的Java版本。通常这里返回SourceVersion.latestSupported()。然而,如果你有足够的理由只支持Java 6的话,你也可以返回SourceVersion.RELEASE_6。推荐使用前者。