我可以用反射或类似的东西来做吗?
答案 0 :(得分:35)
我一直在寻找一段时间,似乎有不同的方法,这里有一个总结:
reflections 库非常受欢迎。它看起来像这样:
Reflections reflections = new Reflections("firstdeveloper.examples.reflections");
Set<Class<? extends Pet>> classes = reflections.getSubTypesOf(Pet.class);
ServiceLoader (根据erickson的回答),它看起来像这样:
ServiceLoader<Pet> loader = ServiceLoader.load(Pet.class);
for (Pet implClass : loader) {
System.out.println(implClass.getClass().getSimpleName()); // prints Dog, Cat
}
请注意,为此,您需要将Pet
定义为ServiceProviderInterface(SPI)并声明其实现。您可以通过在resources/META-INF/services
中创建名为examples.reflections.Pet
的文件并在其中声明Pet
的所有实现来实现此目的
examples.reflections.Dog
examples.reflections.Cat
包级注释。这是一个例子:
Package[] packages = Package.getPackages();
for (Package p : packages) {
MyPackageAnnotation annotation = p.getAnnotation(MyPackageAnnotation.class);
if (annotation != null) {
Class<?>[] implementations = annotation.implementationsOfPet();
for (Class<?> impl : implementations) {
System.out.println(impl.getSimpleName());
}
}
}
和注释定义:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PACKAGE)
public @interface MyPackageAnnotation {
Class<?>[] implementationsOfPet() default {};
}
并且必须在该包内的名为package-info.java
的文件中声明包级注释。这里有样本内容:
@MyPackageAnnotation(implementationsOfPet = {Dog.class, Cat.class})
package examples.reflections;
请注意,只有通过调用Package.getPackages()
才会加载当时ClassLoader已知的软件包。
此外,还有其他基于URLClassLoader的方法,它们将始终限于已经加载的类,除非您进行基于目录的搜索。
答案 1 :(得分:27)
埃里克森说过,但如果你还想这样做,那就看看Reflections。从他们的页面:
使用反射,您可以查询元数据:
- 获取某种类型的所有子类型
- 获取所有注释了一些注释的类型
- 获取所有注释了一些注释的类型,包括匹配的注释参数
- 获取所有使用某些
注释的方法
答案 2 :(得分:24)
一般来说,这样做很昂贵。要使用反射,必须加载该类。如果要加载类路径上可用的每个类,则需要时间和内存,不建议使用。
如果你想避免这种情况,你需要实现自己的类文件解析器,它可以更有效地运行,而不是反射。字节码工程库可能有助于这种方法。
Service Provider mechanism是枚举可插拔服务实现的传统方法。使用Java 6中的ServiceLoader
,或在早期版本中实现自己的an example。我在另一个答案中提供了{{3}}。
答案 3 :(得分:11)
Spring有一种非常简单的方法来实现这个目标:
public interface ITask {
void doStuff();
}
@Component
public class MyTask implements ITask {
public void doStuff(){}
}
然后你可以自动装配类型为ITask
的列表,Spring会用所有实现填充它:
@Service
public class TaskService {
@Autowired
private List<ITask> tasks;
}
答案 4 :(得分:4)
是的,第一步是确定您所关心的“所有”类别。如果您已经拥有此信息,则可以枚举每个信息并使用instanceof验证关系。相关文章如下:http://www.javaworld.com/javaworld/javatips/jw-javatip113.html
答案 5 :(得分:4)
埃里克森说的最好。这是一个相关的问答帖子 - http://www.velocityreviews.com/forums/t137693-find-all-implementing-classes-in-classpath.html
Apache BCEL库允许您在不加载类的情况下读取类。我相信它会更快,因为你应该能够跳过验证步骤。使用类加载器加载所有类的另一个问题是,您将遭受巨大的内存影响,以及无意中运行您可能不想做的任何静态代码块。
Apache BCEL库链接 - http://jakarta.apache.org/bcel/
答案 6 :(得分:1)
此外,如果您正在编写IDE插件(您尝试做的事情相对常见),那么IDE通常会为您提供更有效的方法来访问用户代码当前状态的类层次结构。
答案 7 :(得分:0)
我遇到了同样的问题。我的解决方案是使用反射来检查ObjectFactory类中的所有方法,从而消除那些不是createXXX()方法返回我的一个绑定POJO的实例的方法。如此发现的每个类都被添加到Class []数组中,然后将其传递给JAXBContext实例化调用。这表现很好,只需要加载ObjectFactory类,无论如何都需要它。我只需要维护ObjectFactory类,这是一个手工执行的任务(在我的例子中,因为我从POJO开始并使用了schemagen),或者可以根据需要由xjc生成。无论哪种方式,它都是高效,简单和有效的。
答案 8 :(得分:0)
尝试ClassGraph。 (免责声明,我是作者)。 ClassGraph支持在运行时或构建时扫描实现给定接口的类,但还可以进行更多操作。 ClassGraph可以在内存中,类路径上的所有类或白名单包中的类中构建整个类图(所有类,注释,方法,方法参数和字段)的抽象表示,但是您可以查询该类图你要。 ClassGraph比其他任何扫描仪都支持more classpath specification mechanisms and classloaders,并且还可以与新的JPMS模块系统无缝协作,因此,如果您的代码基于ClassGraph,则代码将具有最大的可移植性。 See the API here.
答案 9 :(得分:0)
@ kaybee99答案的新版本,但现在返回用户要求的内容:实现...
Spring有一个非常简单的方法来实现这一目标:
public interface ITask {
void doStuff();
default ITask getImplementation() {
return this;
}
}
@Component
public class MyTask implements ITask {
public void doStuff(){}
}
然后,您可以自动连接类型为ITask
的列表,Spring将使用所有实现填充它:
@Service
public class TaskService {
@Autowired(required = false)
private List<ITask> tasks;
if ( tasks != null)
for (ITask<?> taskImpl: tasks) {
taskImpl.doStuff();
}
}
答案 10 :(得分:0)
使用ClassGraph很简单:
使用Groovy代码查找my.package.MyInterface
的实现:
@Grab('io.github.classgraph:classgraph:4.6.18')
import io.github.classgraph.*
new ClassGraph().enableClassInfo().scan().getClassesImplementing('my.package.MyInterface').findAll{!it.abstract}*.className