JAVA编译时注解
最近看了几个6.0权限的库,发现一个黑科技,编译时注解,简单了解了一下之后发现,简直就是懒人专用的装逼神器啊,赶紧学一学。
自定义注解相关知识
定义注解的格式:
上面的代码来自鸿洋大神的MPermission,可以看到是一个编译时的方法注解,其中有一个参数。
注解处理器
当我们写好一个注解之后,怎么获取并处理这个注解所对应的数据呢?这就需要一个注解处理器AbstractProcessor,我们可以通过它来获取我们自定义的注解,并且获取数据,生成代码。常见方法:
getSupportedAnnotationTypes():
返回一个set集合,集合内容为自定义注解的包名+类名
|
|
其中(PermissionDenied.class.getCanonicalName()是获取当前class的包名加类名。
getSupportedSourceVersion():返回支持的版本,这个统一返回latestSupported();
init(ProcessingEnvironment processingEnv):初始化方法,可以通过processingEnv获取相关对象,对元素进行操作。
process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv): 参数 Set<? extends TypeElement> annotations :将返回所有的由该Processor处理,并待处理的 Annotations。(属于该Processor处理的注解,但并未被使用,不存在与这个集合里)
参数 RoundEnvironment roundEnv :表示当前或是之前的运行环境,可以通过该对象查找找到的注解。返回值 表示这组 annotations 是否被这个 Processor 接受,如果接受(true)后续子的 pocessor 不会再对这个 Annotations 进行处理。
相关对象:
Messager:控制台输出
Elements:获取元素的相关信息
Filer:获取文件操作对象
Element:表示一个程序元素,比如包、类或者方法。子类类型:
- ExecutableElement:表示某个类或接口的方法、构造方法或初始化程序(静态或实例),包括注释类型元素。对应@Target(ElementType.METHOD)@Target(ElementType.CONSTRUCTOR)
- PackageElement:表示一个包程序元素。提供对有关包极其成员的信息访问。对应@Target(ElementType.PACKAGE)
- TypeElement:表示一个类或接口程序元素。提供对有关类型极其成员的信息访问。对应@Target(ElementType.TYPE)
- TypeParameterElement:表示一般类、接口、方法或构造方法元素的类型参数。对应@Target(ElementType.PARAMETER)
- VariableElement:表示一个字段、enum常量、方法或构造方法参数、局部变量或异常参数。对应@Target(ElementType.LOCAL_VARIABLE)
相应的Target可以进行强转。
获取参数:根据不同的Element获取一些基本信息:包名,类名,方法名,参数类型,返回值。
ExecutableElement:123456789101112131415161718192021222324252627282930313233343536373839404142//OnceClick.class 以 @Target(ElementType.METHOD)修饰for (Element element : roundEnv.getElementsAnnotatedWith(OnceClick.class)) {//对于Element直接强转ExecutableElement executableElement = (ExecutableElement) element;//非对应的Element,通过getEnclosingElement转换获取TypeElement classElement = (TypeElement) element.getEnclosingElement();//当(ExecutableElement) element成立时,使用(PackageElement) element// .getEnclosingElement();将报错。//需要使用elementUtils来获取Elements elementUtils = processingEnv.getElementUtils();PackageElement packageElement = elementUtils.getPackageOf(classElement);//全类名String fullClassName = classElement.getQualifiedName().toString();//类名String className = classElement.getSimpleName().toString();//包名String packageName = packageElement.getQualifiedName().toString();//方法名String methodName = executableElement.getSimpleName().toString();//取得方法参数列表List<? extends VariableElement> methodParameters = executableElement.getParameters();//参数类型列表List<String> types = new ArrayList<>();for (VariableElement variableElement : methodParameters) {TypeMirror methodParameterType = variableElement.asType();if (methodParameterType instanceof TypeVariable) {TypeVariable typeVariable = (TypeVariable) methodParameterType;methodParameterType = typeVariable.getUpperBound();}//参数名String parameterName = variableElement.getSimpleName().toString();//参数类型String parameteKind = methodParameterType.toString();types.add(methodParameterType.toString());}}
VariableElement:
|
|
TypeElement:
|
|
了解了这么多之后,来实战检验一下吧,还是一MPermission为例
|
|
代码里的注释写的很清楚了,不过多介绍,接下来看看proxyInfo.generateJavaCode()方法做了哪些事情
|
|
创建一个StringBuilder对象,拼接出一个一个类的全部内容,代码不难理解。来看看结果是什么样的
|
|
跟上文中所设计的StringBuilder一致,这样我们就通过编译时注解的方式自动生成了一个类。