注解处理
在开发中,碰见很多注解,如@Override
、@Documented
等,还有像现在很多依赖注入库如ARouter,Dagger 2等·,他的内部如何处理自己的注解,通过什么途径获取自己的注解,实现自己的业务逻辑。看一下ARouter,当添加ARouter依赖后,在需要的Activity
添加注解,编译就会看到在build文件夹下生成如下图的文件,文件生成是通过在注解处理器中编写解析和生成代码格式。
注解处理器:
是一个在javac中的,用来编译时扫描和处理的注解的工具。你可以为特定的注解,注册你自己的注解处理器。
注解处理器可以生成Java代码,这些生成的Java代码会组成 .java 文件,但不能修改已经存在的Java类(即不能向已有的类中添加方法)。而这些生成的Java文件,会同时与其他普通的手写Java源代码一起被javac编译。
注解处理器抽象类AbstractProcessor
:
public class AnnotionTest extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
return false;
}
@Override
public Set<String> getSupportedAnnotationTypes() {
return super.getSupportedAnnotationTypes();
}
@Override
public SourceVersion getSupportedSourceVersion() {
return super.getSupportedSourceVersion();
}
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
}
}
init(ProcessingEnvironment processingEnvironment)
初始化数据,相当于构造器,初始化一些工具类,如下。
Map<String, String> getOptions();
Messager getMessager();
Filer getFiler();
Elements getElementUtils();
Types getTypeUtils();
SourceVersion getSourceVersion();
Locale getLocale();
getSupportedAnnotationTypes()
必须重写,指明要注解哪个注解,如
Set<String> setClazzName = new LinkedHashSet<>();
setClazzName.add(TestSelfAnnotation.class.getName());
return setClazzName;
getSupportedSourceVersion()
支持的java版本。
process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment)
业务逻辑的处理。
如何使用注解处理器:
- 首先我们需要创建java库,在库中定义自己的注解,如下:
@Documented
@Target({ElementType.FIELD})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface TestSelfAnnotation {
int value() default 0;
}
- 创建一个java库,添加如下依赖,编写自己的注解处理器继承
AbstractProcessor
,生成代码使用JavaPoet库,生成.java源文件。
compile 'com.google.auto.service:auto-service:1.0-rc4'
compile 'com.squareup:javapoet:1.8.0'
implementation project(':annotationLib')
@AutoService(Processor.class)
public class MyAnnotationProcessor extends AbstractProcessor {
private Map<String, String> getOptions;
private Messager getMessagers;
private Filer getFilers;
private Elements elementUtils;
private Types typeUtils;
private SourceVersion getSourceVersion;
private Locale getLocale;
private static final String RANDOM_SUFFIX = "$$BindView";
private HashMap<String, HashMap<String, String>> entryKey = new HashMap<>();
private HashMap<String, Element> elementIndexMap = new HashMap<>();
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
Set<? extends Element> elementsAnnotatedWith = roundEnvironment.getElementsAnnotatedWith(TestSelfAnnotation.class);
for (final Element element : elementsAnnotatedWith)
if (element.getKind() == ElementKind.FIELD) {
TestSelfAnnotation annotation = element.getAnnotation(TestSelfAnnotation.class);
TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
ClassName className = ClassName.get(enclosingElement);
if (entryKey.containsKey(className.simpleName())) {
HashMap<String, String> stringStringHashMap = entryKey.get(className.simpleName());
stringStringHashMap.put(element.getSimpleName().toString(), annotation.value() + "");
// entryKey.put(element.getEnclosingElement().getSimpleName().toString(), stringStringHashMap);
} else {
HashMap<String, String> hashMap = new HashMap<>();
hashMap.put(element.getSimpleName().toString(), annotation.value() + "");
elementIndexMap.put(className.simpleName(), element);
entryKey.put(className.simpleName(), hashMap);
}
}
writeClazz(entryKey, elementIndexMap);
return false;
}
private void writeClazz(HashMap<String, HashMap<String, String>> entryKey, HashMap<String, Element> elementIndexMap) {
for (Map.Entry<String, HashMap<String, String>> map : entryKey.entrySet()) {
String key = map.getKey();
HashMap<String, String> value = map.getValue();
Element element = elementIndexMap.get(key);
TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
ClassName className = ClassName.get(enclosingElement);
MethodSpec.Builder builder = MethodSpec.constructorBuilder()
.addModifiers(Modifier.PUBLIC)
.addParameter(className, "activity");
if (value.size() > 0) {
for (HashMap.Entry<String, String> entry : value.entrySet()) {
CodeBlock of = CodeBlock.of("$N." + entry.getKey() + "=" + "$N." + "findViewById("
+ entry.getValue() + ");\n", "activity", "activity");
builder.addCode(of);
}
}
TypeSpec build = TypeSpec.classBuilder(getClazzName(element) + RANDOM_SUFFIX)
.addModifiers(Modifier.PUBLIC)
.addMethod(builder.build())
.build();
try {
JavaFile javaFile = JavaFile.builder(getPackageName(element), build).build();
javaFile.writeTo(getFilers);
} catch (IOException e) {
e.printStackTrace();
}
}
}
private String getClazzName(Element element) {
Element enclosingElement = element.getEnclosingElement();
return enclosingElement.getSimpleName().toString();
}
private String getPackageName(Element element) {
PackageElement packageOf = elementUtils.getPackageOf(element);
return packageOf.getQualifiedName().toString();
}
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
getMessagers = processingEnvironment.getMessager();
getFilers = processingEnvironment.getFiler();
elementUtils = processingEnvironment.getElementUtils();
typeUtils = processingEnvironment.getTypeUtils();
}
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> setClazzName = new LinkedHashSet<>();
System.out.println(" " + TestSelfAnnotation.class.getCanonicalName());
setClazzName.add(TestSelfAnnotation.class.getName());
return setClazzName;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.RELEASE_7;
}
}
@AutoService(Processor.class)
帮我们自动生成生成如下文件。
3. module依赖注解后,使用注解,编译,我们可以看到,在apt文件下生成文件。
文件结构如下:
@TestSelfAnnotation(R.id.tv_button)
TextView tv_button;
@TestSelfAnnotation(R.id.bt_ok)
Button bt_button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_shop_main);
HelpAnnotation.inject(this);
}
HelpAnnotation
工具类:
public class HelpAnnotation {
private static final String RANDOM_SUFFIX = "$$BindView";
/**
* 通过反射的方式找到对应的辅助类,并调用对应的方法实现属性的注入
*
* @param object 被注入的对象
*/
public static void inject(Object object) {
try {
Class bindingClass = Class.forName(object.getClass().getCanonicalName() + RANDOM_SUFFIX);
//noinspection unchecked
Constructor constructor = bindingClass.getConstructor(object.getClass());
constructor.newInstance(object);
} catch (ClassNotFoundException e) {
Log.e("TAG", "Meaningful Message", e);
} catch (NoSuchMethodException e) {
Log.e("TAG", "Meaningful Message", e);
} catch (IllegalAccessException e) {
Log.e("TAG", "Meaningful Message", e);
} catch (InstantiationException e) {
Log.e("TAG", "Meaningful Message", e);
} catch (InvocationTargetException e) {
Log.e("TAG", "Meaningful Message", e);
}
}
}
常见的几个API:
Name simpleName = element.getSimpleName();//注解的元素名称
//如果元素在{}内,则返回类名称。
如果这是顶级类型,则返回其包。
如果这是一个包,null则返回。
如果这是一个类型参数, 则返回参数类型。
element.getEnclosingElement();
ClassName className = ClassName.get(enclosingElement);//获得类名