在Eclipse插件中使用时,Reflections库无法正常工作

我使用Reflections库开发了一个应用程序,用于查询具有特定注释的所有类。 在我决定从我的应用程序创建一个Eclipse插件之前,一切都像魅力一样。 然后,思考停止工作。

鉴于我的应用程序在不是Eclipse插件的一部分时工作正常,我认为它应该是一个类加载器问题。 所以我在我的Reflections类中添加了插件激活器类的类加载器,上下文类加载器以及我能想象的所有其他类加载器,没有任何成功。 这是我的代码的简化版本:

 ConfigurationBuilder config = new ConfigurationBuilder(); config.addClassLoaders(thePluginActivatorClassLoader); config.addClassLoaders(ClasspathHelper.getContextClassLoader()); config.addClassLoaders("all the classloaders I could imagine"); config.filterInputsBy(new FilterBuilder().include("package I want to analyze")); Reflections reflections = new Reflections(config); Set<Class> classes = reflections.getTypesAnnotatedWith(MyAnnotation.class); //this Set is empty 

我也尝试将我想加载的类的URL添加到ConfigurationBuilder类,但它没有帮助。

有人能告诉我是否有办法让Reflections作为Eclipse插件的一部分工作?或者我应该更好地寻找另一种替代方案? 非常感谢,我真的很困惑。

我假设您已经知道如何创建包(否则,请检查此 )。

在对Reflections API进行了一些调查和探索之后,我意识到问题是Reflection只是无法读取OSGi URL(bundleresource:// …)导致exception:

 org.reflections.ReflectionsException: could not create Vfs.Dir from url, no matching UrlType was found [bundleresource://1009.fwk651584550/] 

这个建议:

 either use fromURL(final URL url, final List urlTypes) or use the static setDefaultURLTypes(final List urlTypes) or addDefaultURLTypes(UrlType urlType) with your specialized UrlType. 

所以我相信为OSGi实现一个UrlType(例如, class BundleUrlType implements UrlType {...} )并将其注册为:

 Vfs.addDefaultURLTypes(new BundleUrlType()); 

应该使束内的Reflections API可用。 应该将依赖关系反馈添加到Eclipse插件项目中,如此处所述。

这是添加所需jar子后我的样品MANIFEST.MF的样子:

 Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: ReflectivePlugin Bundle-SymbolicName: ReflectivePlugin Bundle-Version: 1.0.0.qualifier Bundle-Activator: reflectiveplugin.Activator Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Import-Package: javax.annotation;version="1.0.0", org.osgi.framework;version="1.3.0", org.osgi.service.log;version="1.3", org.osgi.util.tracker;version="1.3.1" Bundle-ClassPath: ., lib/dom4j-1.6.1.jar, lib/guava-r08.jar, lib/javassist-3.12.1.GA.jar, lib/reflections-0.9.5.jar, lib/slf4j-api-1.6.1.jar, lib/xml-apis-1.0.b2.jar Export-Package: reflectiveplugin, reflectiveplugin.data 

注意:使用Reflections v.9.5.5

以下是UrlType实现的示例:

 package reflectiveplugin; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.Enumeration; import java.util.Iterator; import org.osgi.framework.Bundle; import org.reflections.vfs.Vfs; import org.reflections.vfs.Vfs.Dir; import org.reflections.vfs.Vfs.File; import org.reflections.vfs.Vfs.UrlType; import com.google.common.collect.AbstractIterator; public class BundleUrlType implements UrlType { public static final String BUNDLE_PROTOCOL = "bundleresource"; private final Bundle bundle; public BundleUrlType(Bundle bundle) { this.bundle = bundle; } @Override public boolean matches(URL url) { return BUNDLE_PROTOCOL.equals(url.getProtocol()); } @Override public Dir createDir(URL url) { return new BundleDir(bundle, url); } public class BundleDir implements Dir { private String path; private final Bundle bundle; public BundleDir(Bundle bundle, URL url) { this(bundle, url.getPath()); } public BundleDir(Bundle bundle, String p) { this.bundle = bundle; this.path = p; if (path.startsWith(BUNDLE_PROTOCOL + ":")) { path = path.substring((BUNDLE_PROTOCOL + ":").length()); } } @Override public String getPath() { return path; } @Override public Iterable getFiles() { return new Iterable() { public Iterator iterator() { return new AbstractIterator() { final Enumeration entries = bundle.findEntries(path, "*.class", true); protected Vfs.File computeNext() { return entries.hasMoreElements() ? new BundleFile(BundleDir.this, entries.nextElement()) : endOfData(); } }; } }; } @Override public void close() { } } public class BundleFile implements File { private final BundleDir dir; private final String name; private final URL url; public BundleFile(BundleDir dir, URL url) { this.dir = dir; this.url = url; String path = url.getFile(); this.name = path.substring(path.lastIndexOf("/") + 1); } @Override public String getName() { return name; } @Override public String getRelativePath() { return getFullPath().substring(dir.getPath().length()); } @Override public String getFullPath() { return url.getFile(); } @Override public InputStream openInputStream() throws IOException { return url.openStream(); } } } 

这就是我在Activator类中创建reflection的方法:

 private Reflections createReflections(Bundle bundle) { Vfs.addDefaultURLTypes(new BundleUrlType(bundle)); Reflections reflections = new Reflections(new Object[] { "reflectiveplugin.data" }); return reflections; } 

最后一点非常令人困惑,但仍然很重要:如果你在Eclipse(Run As / OSGi Framework)中运行你的插件,你必须将你的类输出目录添加到Reflections路径模式(即“bin”或“target / classes” “)。 虽然,发布的插件不需要它(构建插件/包做“导出” – >“可部署的插件和片段”)。

只是为了记录,以防其他人有同样的问题。 这里对Vlad的答案进行了一些小修改,以避免必须将输出目录添加到Reflections路径模式中。 区别仅在于BundleDir类。 它在我的所有测试中似乎都运行良好:

 public class BundleUrlType implements UrlType { public static final String BUNDLE_PROTOCOL = "bundleresource"; private final Bundle bundle; public BundleUrlType(Bundle bundle) { this.bundle = bundle; } @Override public Dir createDir(URL url) { return new BundleDir(bundle, url); } @Override public boolean matches(URL url) { return BUNDLE_PROTOCOL.equals(url.getProtocol()); } public static class BundleDir implements Dir { private String path; private final Bundle bundle; private static String urlPath(Bundle bundle, URL url) { try { URL resolvedURL = FileLocator.resolve(url); String resolvedURLAsfile = resolvedURL.getFile(); URL bundleRootURL = bundle.getEntry("/"); URL resolvedBundleRootURL = FileLocator.resolve(bundleRootURL); String resolvedBundleRootURLAsfile = resolvedBundleRootURL.getFile(); return("/"+resolvedURLAsfile.substring(resolvedBundleRootURLAsfile.length())); } catch (IOException e) { throw new RuntimeException(e); } } public BundleDir(Bundle bundle, URL url) { //this(bundle, url.getPath()); this(bundle, urlPath(bundle,url)); } public BundleDir(Bundle bundle, String p) { this.bundle = bundle; this.path = p; if (path.startsWith(BUNDLE_PROTOCOL + ":")) { path = path.substring((BUNDLE_PROTOCOL + ":").length()); } } @Override public String getPath() { return path; } @Override public Iterable getFiles() { return new Iterable() { public Iterator iterator() { return new AbstractIterator() { final Enumeration entries = bundle.findEntries(path, "*.class", true); protected Vfs.File computeNext() { return entries.hasMoreElements() ? new BundleFile(BundleDir.this, entries.nextElement()) : endOfData(); } }; } }; } @Override public void close() { } } public static class BundleFile implements File { private final BundleDir dir; private final String name; private final URL url; public BundleFile(BundleDir dir, URL url) { this.dir = dir; this.url = url; String path = url.getFile(); this.name = path.substring(path.lastIndexOf("/") + 1); } @Override public String getName() { return name; } @Override public String getRelativePath() { return getFullPath().substring(dir.getPath().length()); } @Override public String getFullPath() { return url.getFile(); } @Override public InputStream openInputStream() throws IOException { return url.openStream(); } } } 

Eclipse建立在OSGi之上,你正在反对OSGi类加载……这不是一场轻松的战斗。

看看Neil Bartlett的这篇文章: OSGi准备 – 加载类 。 你也可以谷歌搜索“OSGi伙伴政策”。