在 Eclipse 插件中使用时反射库不起作用
我使用 Reflections 库开发了一个应用程序,用于查询所有具有特定属性的类注解.在我决定从我的应用程序创建一个 Eclipse 插件之前,一切都像魅力一样工作.然后反射停止工作.
I have developed an application using the Reflections library for querying all the classes having a particular annotation. Everything was working like a charm until I decided to create an Eclipse plug-in from my application. Then Reflections stop working.
鉴于我的应用程序在不是 Eclipse 插件的一部分时运行良好,我认为这应该是类加载器问题.因此,我将插件激活器类的类加载器、上下文类加载器以及我能想象到的所有其他类加载器添加到我的 Reflections
类中,但没有任何成功.这是我的代码的简化版本:
Given that my application is working fine when not part of an Eclipse plug-in, I think it should be a class-loader problem.
So I added to my Reflections
class the classloaders of the plug-in activator class, the context class loader, and all other class loaders I could imagine, without any success. This is a simplified version of my code:
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
类,但没有帮助.
I also tried adding URLs of the classes I want to load to the ConfigurationBuilder
class, but it did not help.
有人能告诉我是否有办法让 Reflections
作为 Eclipse 插件的一部分工作吗?还是我应该更好地寻找另一种替代方法?非常感谢,我真的很困惑.
Could someone tell me if there is a way to make Reflections
work as part of an Eclipse plug-in ?, or should I better look for another alternative ?. Thanks a lot, I am really puzzled about it.
推荐答案
我假设您已经知道如何创建捆绑包(否则,请查看 这个).
I assume you already know how to create bundles (otherwise, check this).
在对反射 API 进行一些调试和探索之后,我意识到问题在于反射根本无法读取 OSGi URL (bundleresource://...),从而导致异常:
After some debuging and exploration of Reflections API I have realised that the problem is that Reflections simply fails to read OSGi URLs (bundleresource://...) resulting in an 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<UrlType> urlTypes)
or use the static setDefaultURLTypes(final List<UrlType> urlTypes)
or addDefaultURLTypes(UrlType urlType) with your specialized UrlType.
所以我相信为 OSGi 实现一个 UrlType(例如 class BundleUrlType implements UrlType {...}
)并像这样注册它:
So I believe implementing a UrlType for OSGi (e.g. class BundleUrlType implements UrlType {...}
) and registering it like this:
Vfs.addDefaultURLTypes(new BundleUrlType());
应该使 Reflections API 可以在包中使用.反射依赖项应该添加到 Eclipse 插件项目中,如 这里.
should make Reflections API usable from inside a bundle. Reflections dependencies should be added to the Eclipse Plugin project as described here.
这是我的示例 MANIFEST.MF 添加所需 jar 后的样子:
This is how my sample MANIFEST.MF looked like after adding needed jars:
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
注意:使用反射 v. 0.9.5
Note: Used Reflections v. 0.9.5
这是一个示例 UrlType 实现:
Here's a sample UrlType implementation:
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<File> getFiles() {
return new Iterable<Vfs.File>() {
public Iterator<Vfs.File> iterator() {
return new AbstractIterator<Vfs.File>() {
final Enumeration<URL> 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 类中创建反射的方式:
And this is how I create reflections in the Activator class:
private Reflections createReflections(Bundle bundle) {
Vfs.addDefaultURLTypes(new BundleUrlType(bundle));
Reflections reflections = new Reflections(new Object[] { "reflectiveplugin.data" });
return reflections;
}
最后一点非常令人困惑,但仍然很重要:如果您在 Eclipse(运行方式/OSGi 框架)中运行您的插件,您还必须将您的类输出目录添加到反射路径模式(即bin"或目标/类").虽然,发布的插件不需要它(构建插件/捆绑包执行导出"->可部署插件和片段").
The last bit is very confusing, but still important: if you run your plugin inside of Eclipse (Run As / OSGi Framework) you have to add also your classes output directory to the Reflections path patterns (i.e. "bin" or "target/classes"). Although, it's not needed for a released plugin (to build a plugin/bundle do "Export"->"Deployable plug-ins and fragments").
相关文章