Java 为什么反射包扫描可以在标准JVM中工作而不能在Android中工作

Java 为什么反射包扫描可以在标准JVM中工作而不能在Android中工作,java,android,reflection,Java,Android,Reflection,我已经编写了简单的ReflectionUtils,为了找到带有特定注释的所有类,我在我的服务器上成功地使用了它,但由于某些原因,它在Android上的性能不如预期。(我专门使用它查找用@JsonTypeName注释的所有类,并将它们添加到ObjectMapper上下文中) 有什么问题吗 package com.acme.reflection.utils; import java.io.File; import java.io.IOException; import java.lang.annot

我已经编写了简单的ReflectionUtils,为了找到带有特定注释的所有类,我在我的服务器上成功地使用了它,但由于某些原因,它在Android上的性能不如预期。(我专门使用它查找用@JsonTypeName注释的所有类,并将它们添加到ObjectMapper上下文中)

有什么问题吗

package com.acme.reflection.utils;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.JarURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class ReflectionUtils {

/** URL prefix for loading from the file system: "file:" */
public static final String FILE_URL_PREFIX = "file:";

/** URL protocol for an entry from a jar file: "jar" */
public static final String URL_PROTOCOL_JAR = "jar";

/** URL protocol for an entry from a zip file: "zip" */
public static final String URL_PROTOCOL_ZIP = "zip";

/** URL protocol for an entry from a JBoss jar file: "vfszip" */
public static final String URL_PROTOCOL_VFSZIP = "vfszip";

/** URL protocol for a JBoss VFS resource: "vfs" */
public static final String URL_PROTOCOL_VFS = "vfs";

/** URL protocol for an entry from a WebSphere jar file: "wsjar" */
public static final String URL_PROTOCOL_WSJAR = "wsjar";

/** URL protocol for an entry from an OC4J jar file: "code-source" */
public static final String URL_PROTOCOL_CODE_SOURCE = "code-source";

/** Separator between JAR URL and file path within the JAR */
public static final String JAR_URL_SEPARATOR = "!/";

// Taken from http://stackoverflow.com/questions/1456930/how-do-i-read-all-classes-from-a-java-package-in-the-classpath

public static <T extends Annotation> List<Class<?>> findCandidates(String basePackage, Class<T> searchedAnnotation) {
    ArrayList<Class<?>> candidates = new ArrayList<Class<?>>();
    Enumeration<URL> urls;
    String basePath = basePackage.replaceAll("\\.", File.separator);
    try {
        urls = Thread.currentThread().getContextClassLoader().getResources(basePath);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
    while (urls.hasMoreElements()) {
        URL url = urls.nextElement();
        if (isJarURL(url)) {
            try {
                candidates.addAll(doFindPathMatchingJarResources(url, basePath, searchedAnnotation));
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        } else {
            File directory = new File(url.getFile());
            if (directory.exists() && directory.isDirectory()) {
                for (File file : new File(url.getFile()).listFiles())
                    fetchCandidates(basePackage, file, searchedAnnotation, candidates);
            }
        }
    }
    return candidates;
}

private static <T extends Annotation> void fetchCandidates(String basePackage, File candidate, Class<T> searchedAnnotation, List<Class<?>> candidates) {
    if (candidate.isDirectory()) {
        for (File file : candidate.listFiles())
            fetchCandidates(basePackage + "." + candidate.getName(), file, searchedAnnotation, candidates);
    } else {
        String fileName = candidate.getName();
        if (fileName.endsWith(".class")) {
            String className = fileName.substring(0, fileName.length() - 6);
            Class<?> foundClass = checkCandidate(basePackage + "." + className, searchedAnnotation);

            if (foundClass != null)
                candidates.add(foundClass);
        }
    }
}

public static boolean isJarURL(URL url) {
    String protocol = url.getProtocol();
    return (URL_PROTOCOL_JAR.equals(protocol) || URL_PROTOCOL_ZIP.equals(protocol) || URL_PROTOCOL_WSJAR.equals(protocol) || (URL_PROTOCOL_CODE_SOURCE
            .equals(protocol) && url.getPath().contains(JAR_URL_SEPARATOR)));
}

public static <T extends Annotation> Class<?> checkCandidate(String className, Class<T> searchedAnnotation) {
    try {
        Class<?> candidateClass = Class.forName(className);
        Target target = searchedAnnotation.getAnnotation(Target.class);
        for(ElementType elementType: target.value()) {
            switch(elementType) {
            case TYPE:
                if (candidateClass.getAnnotation(searchedAnnotation) != null)
                    return candidateClass;
                break;
            case CONSTRUCTOR:
                for(Constructor<?> constructor: candidateClass.getConstructors())
                    if(constructor.getAnnotation(searchedAnnotation) != null)
                        return candidateClass;
                break;
            case METHOD:
                for(Method method: candidateClass.getMethods())
                    if(method.getAnnotation(searchedAnnotation) != null)
                        return candidateClass;
                break;
            case FIELD:
                for(Field field: candidateClass.getFields())
                    if(field.getAnnotation(searchedAnnotation) != null)
                        return candidateClass;
                break;
            default:
                break;
            }
        }
    } catch (ClassNotFoundException | NoClassDefFoundError e) {
        ;
    }
    return null;
}

/**
 * Find all resources in jar files that match the given location pattern
 * via the Ant-style PathMatcher.
 * 
 * @param rootDirResource the root directory as Resource
 * @param subPattern the sub pattern to match (below the root directory)
 * @return the Set of matching Resource instances
 * @throws IOException in case of I/O errors
 * @see java.net.JarURLConnection
 * @see org.springframework.util.PathMatcher
 */
protected static <T extends Annotation> Set<Class<?>> doFindPathMatchingJarResources(URL sourceUrl, String basePackage, Class<T> searchedAnnotation)
        throws IOException {

    URLConnection con = sourceUrl.openConnection();
    JarFile jarFile;
    String jarFileUrl;
    String rootEntryPath;
    boolean newJarFile = false;

    if (con instanceof JarURLConnection) {
        // Should usually be the case for traditional JAR files.
        JarURLConnection jarCon = (JarURLConnection) con;
        jarFile = jarCon.getJarFile();
        jarFileUrl = jarCon.getJarFileURL().toExternalForm();
        JarEntry jarEntry = jarCon.getJarEntry();
        rootEntryPath = (jarEntry != null ? jarEntry.getName() : "");
    } else {
        // No JarURLConnection -> need to resort to URL file parsing.
        // We'll assume URLs of the format "jar:path!/entry", with the protocol
        // being arbitrary as long as following the entry format.
        // We'll also handle paths with and without leading "file:" prefix.
        String urlFile = sourceUrl.getFile();
        int separatorIndex = urlFile.indexOf(JAR_URL_SEPARATOR);
        if (separatorIndex != -1) {
            jarFileUrl = urlFile.substring(0, separatorIndex);
            rootEntryPath = urlFile.substring(separatorIndex + JAR_URL_SEPARATOR.length());
            jarFile = getJarFile(jarFileUrl);
        } else {
            jarFile = new JarFile(urlFile);
            jarFileUrl = urlFile;
            rootEntryPath = "";
        }
        newJarFile = true;
    }

    try {
        if (!"".equals(rootEntryPath) && !rootEntryPath.endsWith("/")) {
            // Root entry path must end with slash to allow for proper matching.
            // The Sun JRE does not return a slash here, but BEA JRockit does.
            rootEntryPath = rootEntryPath + "/";
        }
        Set<Class<?>> result = new LinkedHashSet<>(8);
        for (Enumeration<JarEntry> entries = jarFile.entries(); entries.hasMoreElements();) {
            JarEntry entry = entries.nextElement();
            String entryPath = entry.getName();
            if (entryPath.startsWith(rootEntryPath) && entryPath.endsWith(".class")) {
                int entryLength = entryPath.length();
                String className = entryPath.replaceAll(File.separator, ".").substring(0, entryLength - 6);
                Class<?> foundClass = checkCandidate(className, searchedAnnotation);
                if (foundClass != null)
                    result.add(foundClass);
            }
        }
        return result;
    } finally {
        // Close jar file, but only if freshly obtained -
        // not from JarURLConnection, which might cache the file reference.
        if (newJarFile) {
            jarFile.close();
        }
    }
}

/**
 * Resolve the given jar file URL into a JarFile object.
 */
protected static JarFile getJarFile(String jarFileUrl) throws IOException {
    if (jarFileUrl.startsWith(FILE_URL_PREFIX)) {
        try {
            return new JarFile(new URI(jarFileUrl.replaceAll(" ", "%20")).getSchemeSpecificPart());
        } catch (URISyntaxException ex) {
            // Fallback for URLs that are not valid URIs (should hardly ever happen).
            return new JarFile(jarFileUrl.substring(FILE_URL_PREFIX.length()));
        }
    } else {
        return new JarFile(jarFileUrl);
    }
}
package com.acme.reflection.utils;
导入java.io.File;
导入java.io.IOException;
导入java.lang.annotation.annotation;
导入java.lang.annotation.ElementType;
导入java.lang.annotation.Target;
导入java.lang.reflect.Constructor;
导入java.lang.reflect.Field;
导入java.lang.reflect.Method;
导入java.net.JarURLConnection;
导入java.net.URI;
导入java.net.URISyntaxException;
导入java.net.URL;
导入java.net.URLConnection;
导入java.util.ArrayList;
导入java.util.Enumeration;
导入java.util.LinkedHashSet;
导入java.util.List;
导入java.util.Set;
导入java.util.jar.JarEntry;
导入java.util.jar.jar文件;
公共类反射单元{
/**用于从文件系统加载的URL前缀:“文件:”*/
公共静态最终字符串文件\u URL\u PREFIX=“文件:”;
/**jar文件项的URL协议:“jar”*/
公共静态最终字符串URL\u PROTOCOL\u JAR=“JAR”;
/**来自zip文件的条目的URL协议:“zip”*/
公共静态最终字符串URL\u PROTOCOL\u ZIP=“ZIP”;
/**JBoss jar文件项的URL协议:“vfszip”*/
公共静态最终字符串URL_协议_VFSZIP=“VFSZIP”;
/**JBoss VFS资源的URL协议:“VFS”*/
公共静态最终字符串URL\u协议\u VFS=“VFS”;
/**WebSphere jar文件中条目的URL协议:“wsjar”*/
公共静态最终字符串URL\u PROTOCOL\u WSJAR=“WSJAR”;
/**OC4J jar文件项的URL协议:“代码源”*/
公共静态最终字符串URL\u协议\u CODE\u SOURCE=“CODE SOURCE”;
/**JAR URL和JAR内文件路径之间的分隔符*/
公共静态最终字符串JAR_URL_SEPARATOR=“!/”;
//取自http://stackoverflow.com/questions/1456930/how-do-i-read-all-classes-from-a-java-package-in-the-classpath
publicstaticlist>candidates=newarraylistfoundclass=checkCandidate(basePackage+“+”+类名,searchedAnnotation);
if(foundClass!=null)
添加(foundClass);
}
}
}
公共静态布尔值isJarURL(URL URL){
String protocol=url.getProtocol();
返回(URL_协议_JAR.equals(协议)| | URL_协议_ZIP.equals(协议)| | URL_协议_WSJAR.equals(协议)| |(URL_协议|代码|
.equals(协议)和&url.getPath()包含(JAR_url_分隔符));
}
公共静态类检查候选(字符串类名称,类搜索注释){
试一试{
Class candidateClass=Class.forName(className);
Target Target=searchedAnnotation.getAnnotation(Target.class);
对于(ElementType ElementType:target.value()){
开关(元件类型){
案例类型:
if(candidateClass.getAnnotation(searchedAnnotation)!=null)
返回候选类;
打破
案例建构师:
for(构造函数:candidateClass.getConstructors())
if(constructor.getAnnotation(searchedAnnotation)!=null)
返回候选类;
打破
案例法:
对于(方法:candidateClass.getMethods())
if(method.getAnnotation(searchedAnnotation)!=null)
返回候选类;
打破
案例字段:
for(字段:candidateClass.getFields())
if(field.getAnnotation(searchedAnnotation)!=null)
返回候选类;
打破
违约:
打破
}
}
}捕获(ClassNotFoundException | NoClassDefFoundError e){
;
}
返回null;
}
/**
*在jar文件中查找与给定位置模式匹配的所有资源
*通过蚂蚁式路径匹配器。
* 
*@param rootDirResource将根目录作为资源
*@param子模式要匹配的子模式(根目录下)
*@返回匹配的资源实例集
*@在发生I/O错误时引发IOException
*@see java.net.JarURLConnection
*@see org.springframework.util.PathMatcher
*/
受保护的静态集>结果=新LinkedHashSet(8);
对于(枚举项=jarFile.entries();entries.hasMoreElements();){
JarEntry=entries.nextElement();
String entryPath=entry.getName();
if(entryPath.startsWith(rootEntryPath)和&entryPath.endsWith(“.class”)){
int entryLength=entryPath.length();
字符串className=entryPath.replaceAll(File.separator,“.”).substring(0,entryLength-6);
Class foundClass=checkCandidate(类名,searchedAnnotation);
if(foundClass!=null)
结果.添加(foundClass);
}
}
返回结果;
}最后{
//关闭jar文件,但仅当新获取-
//不是来自JarURLConnection,这可能会缓存文件引用。
if(newJarFile){
jarFile.close();
}
}
}
/**
*将给定的jar文件URL解析为JarFile对象。
*/
受保护的静态JarFile getJarFile(字符串jarFileUrl)引发IOException{
if(jarFileUrl.startsWith(文件\ URL \前缀)){
试一试{
返回新的JarFile(新的URI(jarFileUrl.replaceAll(“,“%20”)).getSchemeSpecificPart();
}捕获(URISyntaxException-ex){
//针对无效URI的URL的回退(几乎不会发生)。
返回新的JarFile(jarFileUrl.substring(FILE_URL_PREFIX.length());
}
}否则{
返回新的JarFile(jarFileUrl);
}
}