是否有可能找到给定包中的所有类或接口?(快速看了一下e.g. Package,似乎没有。)
当前回答
春天
这个例子是针对Spring 4的,但是您也可以在早期版本中找到类路径扫描器。
// create scanner and disable default filters (that is the 'false' argument)
final ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
// add include filters which matches all the classes (or use your own)
provider.addIncludeFilter(new RegexPatternTypeFilter(Pattern.compile(".*")));
// get matching classes defined in the package
final Set<BeanDefinition> classes = provider.findCandidateComponents("my.package.name");
// this is how you can load the class type from BeanDefinition instance
for (BeanDefinition bean: classes) {
Class<?> clazz = Class.forName(bean.getBeanClassName());
// ... do your magic with the class ...
}
谷歌番石榴
注意:在版本14中,API仍然被标记为@Beta,所以在生产代码中要小心。
final ClassLoader loader = Thread.currentThread().getContextClassLoader();
for (final ClassPath.ClassInfo info : ClassPath.from(loader).getTopLevelClasses()) {
if (info.getName().startsWith("my.package.")) {
final Class<?> clazz = info.load();
// do something with your clazz
}
}
其他回答
在使用Maven时,Aleksander Blomskøld的解决方案对我的参数化测试@RunWith(parameterized .class)不起作用。正确命名了测试,并显示了找到但未执行的位置:
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running some.properly.named.test.run.with.maven.SomeTest
Tests run: 0, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.123 sec
这里也报道了类似的问题。
在我的例子中,@Parameters是在包中创建每个类的实例。这些测试在IDE中本地运行时运行良好。然而,当运行Maven时,在Aleksander Blomskøld的解决方案中没有找到类。
我确实用以下剪辑完成了它,灵感来自David Pärsson对Aleksander Blomskøld回答的评论:
Reflections reflections = new Reflections(new ConfigurationBuilder()
.setScanners(new SubTypesScanner(false /* don't exclude Object.class */), new ResourcesScanner())
.addUrls(ClasspathHelper.forJavaClassPath())
.filterInputsBy(new FilterBuilder()
.include(FilterBuilder.prefix(basePackage))));
Set<Class<?>> subTypesOf = reflections.getSubTypesOf(Object.class);
我整理了一个简单的github项目来解决这个问题:
https://github.com/ddopson/java-class-enumerator
它既适用于基于文件的类路径,也适用于jar文件。
如果你在签出项目后运行'make',它将打印出以下内容:
Cleaning...
rm -rf build/
Building...
javac -d build/classes src/pro/ddopson/ClassEnumerator.java src/test/ClassIShouldFindOne.java src/test/ClassIShouldFindTwo.java src/test/subpkg/ClassIShouldFindThree.java src/test/TestClassEnumeration.java
Making JAR Files...
jar cf build/ClassEnumerator_test.jar -C build/classes/ .
jar cf build/ClassEnumerator.jar -C build/classes/ pro
Running Filesystem Classpath Test...
java -classpath build/classes test.TestClassEnumeration
ClassDiscovery: Package: 'test' becomes Resource: 'file:/Users/Dopson/work/other/java-class-enumeration/build/classes/test'
ClassDiscovery: Reading Directory '/Users/Dopson/work/other/java-class-enumeration/build/classes/test'
ClassDiscovery: FileName 'ClassIShouldFindOne.class' => class 'test.ClassIShouldFindOne'
ClassDiscovery: FileName 'ClassIShouldFindTwo.class' => class 'test.ClassIShouldFindTwo'
ClassDiscovery: FileName 'subpkg' => class 'null'
ClassDiscovery: Reading Directory '/Users/Dopson/work/other/java-class-enumeration/build/classes/test/subpkg'
ClassDiscovery: FileName 'ClassIShouldFindThree.class' => class 'test.subpkg.ClassIShouldFindThree'
ClassDiscovery: FileName 'TestClassEnumeration.class' => class 'test.TestClassEnumeration'
Running JAR Classpath Test...
java -classpath build/ClassEnumerator_test.jar test.TestClassEnumeration
ClassDiscovery: Package: 'test' becomes Resource: 'jar:file:/Users/Dopson/work/other/java-class-enumeration/build/ClassEnumerator_test.jar!/test'
ClassDiscovery: Reading JAR file: '/Users/Dopson/work/other/java-class-enumeration/build/ClassEnumerator_test.jar'
ClassDiscovery: JarEntry 'META-INF/' => class 'null'
ClassDiscovery: JarEntry 'META-INF/MANIFEST.MF' => class 'null'
ClassDiscovery: JarEntry 'pro/' => class 'null'
ClassDiscovery: JarEntry 'pro/ddopson/' => class 'null'
ClassDiscovery: JarEntry 'pro/ddopson/ClassEnumerator.class' => class 'null'
ClassDiscovery: JarEntry 'test/' => class 'null'
ClassDiscovery: JarEntry 'test/ClassIShouldFindOne.class' => class 'test.ClassIShouldFindOne'
ClassDiscovery: JarEntry 'test/ClassIShouldFindTwo.class' => class 'test.ClassIShouldFindTwo'
ClassDiscovery: JarEntry 'test/subpkg/' => class 'null'
ClassDiscovery: JarEntry 'test/subpkg/ClassIShouldFindThree.class' => class 'test.subpkg.ClassIShouldFindThree'
ClassDiscovery: JarEntry 'test/TestClassEnumeration.class' => class 'test.TestClassEnumeration'
Tests Passed.
另见我的另一个答案
我一直在尝试使用Reflections库,但在使用它时遇到了一些问题,而且为了简单地获取包上的类,我应该包含太多的jar。
我将在这个重复的问题中发布一个解决方案:如何在包中获得所有类的名称?
答案由sp00m撰写;我添加了一些更正,使其工作:
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;
public final class ClassFinder {
private final static char DOT = '.';
private final static char SLASH = '/';
private final static String CLASS_SUFFIX = ".class";
private final static String BAD_PACKAGE_ERROR = "Unable to get resources from path '%s'. Are you sure the given '%s' package exists?";
public final static List<Class<?>> find(final String scannedPackage) {
final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
final String scannedPath = scannedPackage.replace(DOT, SLASH);
final Enumeration<URL> resources;
try {
resources = classLoader.getResources(scannedPath);
} catch (IOException e) {
throw new IllegalArgumentException(String.format(BAD_PACKAGE_ERROR, scannedPath, scannedPackage), e);
}
final List<Class<?>> classes = new LinkedList<Class<?>>();
while (resources.hasMoreElements()) {
final File file = new File(resources.nextElement().getFile());
classes.addAll(find(file, scannedPackage));
}
return classes;
}
private final static List<Class<?>> find(final File file, final String scannedPackage) {
final List<Class<?>> classes = new LinkedList<Class<?>>();
if (file.isDirectory()) {
for (File nestedFile : file.listFiles()) {
classes.addAll(find(nestedFile, scannedPackage));
}
//File names with the $1, $2 holds the anonymous inner classes, we are not interested on them.
} else if (file.getName().endsWith(CLASS_SUFFIX) && !file.getName().contains("$")) {
final int beginIndex = 0;
final int endIndex = file.getName().length() - CLASS_SUFFIX.length();
final String className = file.getName().substring(beginIndex, endIndex);
try {
final String resource = scannedPackage + DOT + className;
classes.add(Class.forName(resource));
} catch (ClassNotFoundException ignore) {
}
}
return classes;
}
}
要使用它,只需调用本例中提到的sp00n的find方法: 如果需要的话,我还添加了类实例的创建。
List<Class<?>> classes = ClassFinder.find("com.package");
ExcelReporting excelReporting;
for (Class<?> aClass : classes) {
Constructor constructor = aClass.getConstructor();
//Create an object of the class type
constructor.newInstance();
//...
}
是的,你可以使用很少的API,这是我喜欢做的,面对这个问题,我使用hibernate核心&必须找到类,其中注释了某个注释。
使这些自定义注释使用,您将标记哪些类您想要获得。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface EntityToBeScanned {
}
然后用它来标记你的课
@EntityToBeScanned
public MyClass{
}
创建具有以下方法的实用程序类
public class ClassScanner {
public static Set<Class<?>> allFoundClassesAnnotatedWithEntityToBeScanned(){
Reflections reflections = new Reflections(".*");
Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(EntityToBeScanned.class);
return annotated;
}
}
调用allfoundclassesannotatedwithentitytobescans()方法获取找到的类集。
你将需要下列的参考书目
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>21.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.javassist/javassist -->
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.22.0-CR1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.reflections/reflections -->
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.10</version>
</dependency>
您可能应该看看开源的Reflections库。有了它,你可以很容易地实现你想要的。
首先,设置反射索引(这有点乱,因为搜索所有类默认是禁用的):
List<ClassLoader> classLoadersList = new LinkedList<ClassLoader>();
classLoadersList.add(ClasspathHelper.contextClassLoader());
classLoadersList.add(ClasspathHelper.staticClassLoader());
Reflections reflections = new Reflections(new ConfigurationBuilder()
.setScanners(new SubTypesScanner(false /* don't exclude Object.class */), new ResourcesScanner())
.setUrls(ClasspathHelper.forClassLoader(classLoadersList.toArray(new ClassLoader[0])))
.filterInputsBy(new FilterBuilder().include(FilterBuilder.prefix("org.your.package"))));
然后你可以查询给定包中的所有对象:
Set<Class<?>> classes = reflections.getSubTypesOf(Object.class);
推荐文章
- URL从Java中的类路径加载资源
- .toArray(new MyClass[0]) or .toArray(new MyClass[myList.size()])?
- Hibernate中不同的保存方法之间有什么区别?
- Java 8流和数组操作
- Java Regex捕获组
- 如何导入本地包没有gopath
- Openssl不被视为内部或外部命令
- 如何添加自定义方法到Spring Data JPA
- 如何在Ubuntu中设置Java环境路径
- 无法执行dex:在Eclipse中超过GC开销限制
- 有人能解释一下JPA和Hibernate中的mappedBy吗?
- 是什么导致JNI调用变慢?
- 如何从Python包内读取(静态)文件?
- Java中的&和&&有什么区别?
- 使用Java的Collections.singletonList()?