最近楼主在使用APT写一个DI框架,这个框架主要是用于楼主的毕设项目中。但是在开发这个框架时,遇到一个关键的问题,就是调用Annotation
的方法来获取一个Class
对象时,结果在编译时直接报错了,抛的异常就是MirroredTypeException
异常。本文的目的是解决这个问题。
本文参考资料:
1.提出问题
楼主自定义了一个Annotation
,这个Annotation
需要传递一个Class对象。如下:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Container {
Class<?> value();
}
然后,我在这个注解来标记一个类,比如说MainActivity
:
@Container(MainActivity.Context.class)
public class MainActivity extends AppCompatActivity {
@Inject
String string1;
@Inject("string")
String string2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public static class Context {
@Provides
String string1;
@Provides("string")
String string2;
}
}
最后,再通过APT来扫描注解,拿到Container
注解的对象,进而获取它内部的Class对象,也就是MainActivity.Context
的Class对象。一切都是那么的美好,但是现实的残酷给我们当头一棒。就在我们开心的build工程时,一个大大的异常出现我们的眼前--MirroredTypeException
。
刚开始出现这个异常时,我是一脸懵逼的。当时简单百度了一下,说是该类还未编译,不能获取它的Class对象。说句实话,当时非常的沮丧,框架的大概结构都已经固定了,突然出现一个这个问题,就意味着当前这条路是不通的。
不过好在天无绝人之路,我找到了解决方法。
1. 解决问题
根据 Getting Class values from Annotations in an AnnotationProcessor这篇文章,可以找到两种解决办法。我们来看看,不过在看解决方法之前,我们还是先来看看之前,我们通过Annation
怎么获取Class对象。
Class<?> clazz = key.getAnnotation(Container.class).value();
正常来说,这一句是没有毛病,可就是在编译时会抛MirroredTypeException
异常,这也是本文需要解决的问题。现在我们来看一下解决方案,一共有两种。
(1). 通过AnnotationMirrors来获取
从参看文章,我们首先可以找到第一种方法,就是通过AnnotationMirrors
来获取。我们来看看代码:
private String getClassFromAnnotation(Element key) {
List<? extends AnnotationMirror> annotationMirrors = key.getAnnotationMirrors();
for (AnnotationMirror annotationMirror : annotationMirrors) {
if (Container.class.getName().equals(annotationMirror.getAnnotationType().toString())) {
Set<? extends ExecutableElement> keySet = annotationMirror.getElementValues().keySet();
for (ExecutableElement executableElement : keySet) {
if (Objects.equals(executableElement.getSimpleName().toString(), "value")) {
return annotationMirror.getElementValues().get(executableElement).getValue().toString();
}
}
}
}
return null;
}
上面的代码我们能够成功获取这个Class
对象的name
字段,更别说Class
对象。其中annotationMirror.getElementValues().get(executableElement)
返回的是一个Object
对象,所以如果是Class<?>[]
类型的话,可以直接进行强转。这里之所强调这一点,是跟第二种解决方案区别开来。
(2). 通过MirroredTypeException异常来获取
当我们通过如上的方法获取Class对象时,会抛出MirroredTypeException
异常,我们可以根据MirroredTypeException
携带的相关信息来获取我们想要的Class对象。
private String getClassFromAnnotationV2(Element key) {
try {
key.getAnnotation(Container.class).value();
} catch (MirroredTypeException e) {
TypeMirror typeMirror = e.getTypeMirror();
return typeMirror.toString();
}
return null;
}
这种方法比较trick,但是不能否认,这也是一种解决方案,而且比第一种方法更加的简单。这里需要的注意的是:
MirroredTypeException
不能获取Class对象,只能获取相应的ClassName,其次它只能获取Class对象的ClassName
,而不能获取Class数组对象的ClassName
,如果想要获取Class数组对象的ClassName
,可以通过MirroredTypesException
来获取。