我们经常会在java代码里面看到:“@Override”,“@Target”等等样子的东西,这些是什么?

在java里面它们是“注解”。

下面是百度百科的解释:java.lang.annotation.Retention可以在您定义Annotation型态时,指示编译器如何对待您的自定义 Annotation,

预设上编译器会将Annotation资讯留在class档案中,但不被虚拟机器读取,而仅用于编译器或工具程式运行时提供资讯。

也就是说,注解是建立在class文件基础上的东西,同C语言的宏有异曲同工的效果。

class文件里面根本看不到注解的痕迹。

注解的基础就是反射。所以注解可以理解为java特有的一种概念。

1.元注解

在java.lang.annotation包里面,已经定义了4种annotation的“原语”。

1).@Target,用于明确被修饰的类型:(方法,字段,类,接口等等)

2).@Retention,描述anntation存在的为止:

RetentionPolicy.RUNTIME 注解会在class字节码文件中存在,在运行时可以通过反射获取到

RetentionPolicy.CLASS 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得

RetentionPolicy.SOURCE 注解仅存在于源码中,在class字节码文件中不包含

3).@Documented,默认情况下,注解不会在javadoc中记录,但是可以通过这个注解来表明这个注解需要被记录。

4).@Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。

如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。

2.自定义注解

package com.joyfulmath.jvmexample.annnotaion;

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

/**

* @author deman.lu

* @version on 2016-05-23 13:36

*/

@Target(ElementType.FIELD)

@Retention(RetentionPolicy.RUNTIME)

public @interface FruitName {

String value() default "";

}

首先,一个注解一般需要2个元注解修饰:

@Target(ElementType.FIELD)

@Retention(RetentionPolicy.RUNTIME)

具体作用上面已解释。

所有的注解都会有一个类似于“func”的部分。这个可以理解为注解的参数。

package com.joyfulmath.jvmexample.annnotaion;

import com.joyfulmath.jvmexample.TraceLog;

/**

* @author deman.lu

* @version on 2016-05-23 13:37

*/

public class Apple {

@FruitName("Apple")

String appleName;

public void displayAppleName()

{

TraceLog.i(appleName);

}

}

这段代码的log:

05-23 13:39:38.780 26792-26792/com.joyfulmath.jvmexample I/Apple: displayAppleName: null [at (Apple.java:16)]

没有赋值成功,为什么?应为注解的“Apple”到底怎么赋值该filed,目前编译器还不知道则怎么做呢。

3.注解处理器

我们还需要一个处理器来解释 注解到底是怎样工作的,不然就跟注释差不多了。

通过反射的方式,可以获取注解的内容:

package com.joyfulmath.jvmexample.annnotaion;

import com.joyfulmath.jvmexample.TraceLog;

import java.lang.reflect.Field;

/**

* @author deman.lu

* @version on 2016-05-23 14:08

*/

public class FruitInfoUtils {

public static void getFruitInfo(Class> clazz)

{

String fruitNameStr = "";

Field[] fields = clazz.getDeclaredFields();

for(Field field:fields)

{

if(field.isAnnotationPresent(FruitName.class))

{

FruitName fruitName = field.getAnnotation(FruitName.class);

fruitNameStr = fruitName.value();

TraceLog.i(fruitNameStr);

}

}

}

}

这是注解的一般用法。

android注解框架解析

从上面可以看到,注解框架的使用,本质上还是要用到反射。

但是我如果用反射的功能在使用注解框架,那么,我还不如直接使用它,反而简单。

如果有一种机制,可以避免写大量重复的相似代码,尤其在android开发的时候,大量的findviewbyid & onClick等事件相应。

代码的模式是一致的,但是代码又各不相同,这个时候,使用注解框架可以大量节省开发时间,当然相应的会增加其他的开销。

以下就是一个使用butterknife的例子:

@BindString(R.string.login_error)

String loginErrorMessage;

看上去很简单,就是把字符串赋一个string res对应的初值。这样写可以节省一些时间。当然这只是一个例子,

如果大量使用其他的注解,可以节省很大一部分的开发时间。

我们下面来看看怎么实现的:

package butterknife;

import android.support.annotation.StringRes;import java.lang.annotation.Retention;

import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.FIELD;

import static java.lang.annotation.RetentionPolicy.CLASS;

/**

* Bind a field to the specified string resource ID.

*

* {@literal @}BindString(R.string.username_error) String usernameErrorText;

*

*/

@Retention(CLASS) @Target(FIELD)

public @interface BindString {

/** String resource ID to which the field will be bound. */

@StringRes int value();

}

BindString,只有一个参数,value,也就是赋值为@StringRes.

同上,上面是注解定义和使用的地方,但是真正解释注解的地方如下:ButterKnifeProcessor

private Map findAndParseTargets(RoundEnvironment env)

这个函数,截取部分代码:

// Process each @BindString element.

for (Element element : env.getElementsAnnotatedWith(BindString.class)) {

if (!SuperficialValidation.validateElement(element)) continue;

try {

parseResourceString(element, targetClassMap, erasedTargetNames);

} catch (Exception e) {

logParsingError(element, BindString.class, e);

}

}

找到所有BindString注解的元素,然后开始分析:

private void parseResourceString(Element element, Map targetClassMap,

Set erasedTargetNames) {

boolean hasError = false;

TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();

// Verify that the target type is String.

if (!STRING_TYPE.equals(element.asType().toString())) {

error(element, "@%s field type must be 'String'. (%s.%s)",

BindString.class.getSimpleName(), enclosingElement.getQualifiedName(),

element.getSimpleName());

hasError = true;

}

// Verify common generated code restrictions.

hasError |= isInaccessibleViaGeneratedCode(BindString.class, "fields", element);

hasError |= isBindingInWrongPackage(BindString.class, element);

if (hasError) {

return;

}

// Assemble information on the field.

String name = element.getSimpleName().toString();

int id = element.getAnnotation(BindString.class).value();

BindingClass bindingClass = getOrCreateTargetClass(targetClassMap, enclosingElement);

FieldResourceBinding binding = new FieldResourceBinding(id, name, "getString", false);

bindingClass.addResource(binding);

erasedTargetNames.add(enclosingElement);

}

首先验证element是不是string类型。

// Assemble information on the field.

String name = element.getSimpleName().toString();

int id = element.getAnnotation(BindString.class).value();

获取field的name,以及 string id。

最终

Map targetClassMap

元素和注解描述,已map的方式一一对应存放。

@Override public boolean process(Set extends TypeElement> elements, RoundEnvironment env) {

Map targetClassMap = findAndParseTargets(env);

for (Map.Entry entry : targetClassMap.entrySet()) {

TypeElement typeElement = entry.getKey();

BindingClass bindingClass = entry.getValue();

try {

bindingClass.brewJava().writeTo(filer);

} catch (IOException e) {

error(typeElement, "Unable to write view binder for type %s: %s", typeElement,

e.getMessage());

}

}

return true;

}

这就是注解框架启动的地方,一个独立的进程。具体细节本文不研究,只需清除,这里是框架驱动的地方。

从上面的信息已经清除,所有的注解信息都存放在targetClassMap 里面。

上面标红的代码,应该是注解框架的核心之处。

自从Java SE5开始,Java就引入了apt工具,可以对注解进行预处理,Java SE6,更是支持扩展注解处理器,

并在编译时多趟处理,我们可以使用自定义注解处理器,在Java编译时,根据规则,生成新的Java代码。

JavaFile brewJava() {

TypeSpec.Builder result = TypeSpec.classBuilder(generatedClassName)

.addModifiers(PUBLIC);

if (isFinal) {

result.addModifiers(Modifier.FINAL);

} else {

result.addTypeVariable(TypeVariableName.get("T", targetTypeName));

}

TypeName targetType = isFinal ? targetTypeName : TypeVariableName.get("T");

if (hasParentBinding()) {

result.superclass(ParameterizedTypeName.get(parentBinding.generatedClassName, targetType));

} else {

result.addSuperinterface(ParameterizedTypeName.get(VIEW_BINDER, targetType));

}

result.addMethod(createBindMethod(targetType));

if (isGeneratingUnbinder()) {

result.addType(createUnbinderClass(targetType));

} else if (!isFinal) {

result.addMethod(createBindToTargetMethod());

}

return JavaFile.builder(generatedClassName.packageName(), result.build())

.addFileComment("Generated code from Butter Knife. Do not modify!")

.build();

}

这段话的关键是会create一个新文件。

然后把相关内容写入。

参考:

https://github.com/JakeWharton/butterknife

erased.java_Java注解一谈相关推荐

  1. redission java_Java注解如何基于Redission实现分布式锁

    这篇文章主要介绍了Java注解如何基于Redission实现分布式锁,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1.定义注解类 @Target( ...

  2. 注解java_Java注解教程及自定义注解

    Java注解提供了关于代码的一些信息,但并不直接作用于它所注解的代码内容.在这个教程当中,我们将学习Java的注解,如何定制注解,注解的使用以及如何通过反射解析注解. Java1.5引入了注解,当前许 ...

  3. Spring @Transactional注解浅谈

    引言: 在Spring中@Transactional提供一种控制事务管理的快捷手段,但是很多人都只是@Transactional简单使用,并未深入了解,其各个配置项的使用方法,本文将深入讲解各个配置项 ...

  4. serialize java_java serialize 浅谈

    对象的串行化(Serialization) 一.串行化的概念和目的 1.什么是串行化 对象的寿命通常随着生成该对象的程序的终止而终止.有时候,可能需要将对象的状态保存下来,在需要时再将对象恢复.我们把 ...

  5. JavaScript, ABAP和Scala里的尾递归(Tail Recursion)

    这是Jerry 2021年的第 12 篇文章,也是汪子熙公众号总共第 283 篇原创文章. 今天是2021年1月20日,看看历史上的今天都发生了什么. 2004年1月20日,第一个公开版本的Scala ...

  6. ProGuard常见问题及解决套路

    ProGuard是一个压缩.优化和混淆Java字节码的工具,非常好用.本篇文章总结一下许多人在使用ProGuard时经常遇到的问题. 我把在使用ProGuard时经常遇到的问题分为两类,分别是导致构建 ...

  7. Android混淆总结

    Proguard 混淆工具来帮助我们快速地对代码进行混淆.根据 Java 官方介绍,Proguard 对应的具体中文定义如下: 1.它是一个包含代码文件压缩.优化.混淆和校验等功能的工具 2.它能够检 ...

  8. 揭秘 Compose 原理,理解 Composable 的本质

    你好,我是朱涛. 今年的Google I/O大会上,Android官方针对Jetpack Compose给出了一系列的性能优化建议,文档和视频都已经放出来了.总的来说,官方的内容都非常棒,看完以后我也 ...

  9. 如何优化 Compose 的性能?通过「底层原理」寻找答案 | 开发者说·DTalk

    本文原作者: 朱涛,原文发布于: 朱涛的自习室‍ 今年的 Google I/O 大会上,Android 官方针对 Jetpack Compose 给出了一系列的性能优化建议,文档和视频都已经放出来了. ...

最新文章

  1. BCompare日志
  2. 如何用C语言编写PHP扩展的详解
  3. 在一个热图中使用多个颜色主题
  4. 现在,你可以撸机器猫了
  5. [NOIP2009 最优贸易]
  6. linux高编IO-------空洞文件
  7. 【数字信号调制】基于matlab GUI PCM编码调制【含Matlab源码 453期】
  8. Google 谷歌翻译 API
  9. 三次Hermite插值
  10. 强化学习: Q-learning实例python实现
  11. hssfrow 单元格样式_poi导出excel单元格中画斜线_AnyReport报表
  12. 负折射率波导matlab,介质波导的数值模拟
  13. html怎么用css文件怎么打开,css文件用什么打开?
  14. 【Wechat】微信小程序注册以及认证流程
  15. 命名规范 (C++)
  16. 【STL】11 list容器操作
  17. jquery 自动触发a 标签的click()方法
  18. picker插件 vue 移动端_基于 vue 的 picker 组件 vue-awesome-picker
  19. 【python-docx 05】操作页眉和页脚
  20. 图的遍历——深度优先搜索和广度(宽度)优先搜索(含例题)

热门文章

  1. fedora配置阿里云镜像
  2. 硬盘插上找不到盘符或不能识别解决方法
  3. java中按学号查找_Java课程设计---浏览学生(实现根据姓名查询)
  4. 移动OA系统,开启办公自由便捷高效之门
  5. 4g hspa android,最新产品 14.4M 4G无线上网卡WCDMA HSPA+
  6. 为什么 Git 比 SVN 好
  7. iPhone 4 4.2.1越狱教程(RedSn0w 0.9.7b4)(转)
  8. BAT 招聘iOS职位要求
  9. 如何评价OA系统的易用性
  10. BlinnPhone高光Shader解读