Java自定义注释和动态加载

Aud*_*ius 6 java reflection android

我正在尝试开发用于数据库同步的ORM,并决定给Java反射.我有一个库,它定义了这样的同步注释

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
public @interface Synchronised {
    String tableName();
    SynchronisationType synchronisationType();
}
Run Code Online (Sandbox Code Playgroud)

在android项目中,我使用此注释来标记模型类

@Synchronised(tableName="UserAccounts", synchronisationType=SynchronisationType.DownloadOnly)
public class UserAccount {   
    @SynchronisedField(fieldName="CompanyFk")
    private int companyId;
    @SynchronisedField(fieldName="Id")
    private int userId;
    @SynchronisedField(fieldName="Username")
    private String username;
    @SynchronisedField(fieldName="Password")
    private String password;
    @SynchronisedField(fieldName="Salt")
    private String salt;
    @SynchronisedField(fieldName="IsLocked")
    private boolean isLocked;
    @SynchronisedField(fieldName="HasMobileAccess")
    private boolean hasMobileAccess;
}
Run Code Online (Sandbox Code Playgroud)

经过一番调查后,最终编写了一个"loader"方法,允许在当前的apk中发现模型类.它应该检索标记为"已同步"的所有类,但这里的问题是getAttribute(Synchronised.class)不起作用.手动迭代注释并搜索属性(instanceof等)也不起作用.在调试时我注意到来自反射的注释实际上是一个代理(getClass()给出"类Proxy2",而annotation.AnnotationType()返回正确的名称) - 这解释了为什么我以前的尝试没有成功.当试图直接投射时 - 投掷异常被抛出(可以理解).基本上我在这里不知所措,所以欢迎任何想法.

模型加载方法(在库项目中):

public static List<Class> getModels(Context context, String packageName)
        throws IOException, URISyntaxException, ClassNotFoundException,
            NameNotFoundException {

        String apkName = context.getPackageManager().getApplicationInfo(packageName, 0).sourceDir;
        DexFile dexFile = new DexFile(apkName);
        //PathClassLoader classLoader = new PathClassLoader(apkName, ClassLoader.getSystemClassLoader());       
        PathClassLoader classLoader = new PathClassLoader(apkName, Thread.currentThread().getContextClassLoader());

        List<Class> classes = new ArrayList<Class>();
        Enumeration<String> entries = dexFile.entries();

        while (entries.hasMoreElements()) {

            String entry = entries.nextElement();
            // only check items that exist in source package and not in libraries, etc.
            if (entry.startsWith(packageName)) {
                Log.d(TAG, "Entry: " + entry);

                Class<?> entryClass = classLoader.loadClass(entry);//dexFile.loadClass(entry, classLoader);
                if (entryClass != null) {
                    Annotation[] annotations = entryClass.getAnnotations();
                    for (Annotation annotation : annotations) {
                        if (annotation instanceof Synchronised) {
                            classes.add(entryClass);
                        }
                    }
                }
            }               
        }

        return classes;     
    }
Run Code Online (Sandbox Code Playgroud)

[解决方案]类加载器需要链接如下:

PathClassLoader classLoader2 = new PathClassLoader(apkName, Thread.currentThread().getContextClassLoader());
DexClassLoader classLoader = new DexClassLoader(apkName, new ContextWrapper(context).getCacheDir().getAbsolutePath(), null, classLoader2);
Run Code Online (Sandbox Code Playgroud)

如果你仍然不知道我在说什么,感谢你阅读这篇冗长的文章( - .

Pet*_*ego 2

仅获取您感兴趣的注释:

Annotation<Synchronised> annotation = entryClass.getAnnotation(Synchronised.class);
Run Code Online (Sandbox Code Playgroud)

更新:

问题是 DexFile.getClass() 显然返回一个代理。OP已找到答案并用解决方案更新了问题。

  • 如果 DexClass 确实返回代理,请尝试以不同的方式加载类:http://stackoverflow.com/questions/3022454/how-to-load-a-java-class-dynamically-on-android-dalvik/3024261#3024261 (2认同)