Java 为什么反射包扫描可以在标准JVM中工作而不能在Android中工作
我已经编写了简单的ReflectionUtils,为了找到带有特定注释的所有类,我在我的服务器上成功地使用了它,但由于某些原因,它在Android上的性能不如预期。(我专门使用它查找用@JsonTypeName注释的所有类,并将它们添加到ObjectMapper上下文中) 有什么问题吗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
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);
}
}