// 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 ...
}
Google Guava
Note: 在版本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
}
}
/**
* Private helper method
*
* @param directory
* The directory to start with
* @param pckgname
* The package name to search for. Will be needed for getting the
* Class object.
* @param classes
* if a file isn't loaded but still is in the directory
* @throws ClassNotFoundException
*/
private static void checkDirectory(File directory, String pckgname,
ArrayList<Class<?>> classes) throws ClassNotFoundException {
File tmpDirectory;
if (directory.exists() && directory.isDirectory()) {
final String[] files = directory.list();
for (final String file : files) {
if (file.endsWith(".class")) {
try {
classes.add(Class.forName(pckgname + '.'
+ file.substring(0, file.length() - 6)));
} catch (final NoClassDefFoundError e) {
// do nothing. this class hasn't been found by the
// loader, and we don't care.
}
} else if ((tmpDirectory = new File(directory, file))
.isDirectory()) {
checkDirectory(tmpDirectory, pckgname + "." + file, classes);
}
}
}
}
/**
* Private helper method.
*
* @param connection
* the connection to the jar
* @param pckgname
* the package name to search for
* @param classes
* the current ArrayList of all classes. This method will simply
* add new classes.
* @throws ClassNotFoundException
* if a file isn't loaded but still is in the jar file
* @throws IOException
* if it can't correctly read from the jar file.
*/
private static void checkJarFile(JarURLConnection connection,
String pckgname, ArrayList<Class<?>> classes)
throws ClassNotFoundException, IOException {
final JarFile jarFile = connection.getJarFile();
final Enumeration<JarEntry> entries = jarFile.entries();
String name;
for (JarEntry jarEntry = null; entries.hasMoreElements()
&& ((jarEntry = entries.nextElement()) != null);) {
name = jarEntry.getName();
if (name.contains(".class")) {
name = name.substring(0, name.length() - 6).replace('/', '.');
if (name.contains(pckgname)) {
classes.add(Class.forName(name));
}
}
}
}
/**
* Attempts to list all the classes in the specified package as determined
* by the context class loader
*
* @param pckgname
* the package name to search
* @return a list of classes that exist within that package
* @throws ClassNotFoundException
* if something went wrong
*/
public static ArrayList<Class<?>> getClassesForPackage(String pckgname)
throws ClassNotFoundException {
final ArrayList<Class<?>> classes = new ArrayList<Class<?>>();
try {
final ClassLoader cld = Thread.currentThread()
.getContextClassLoader();
if (cld == null)
throw new ClassNotFoundException("Can't get class loader.");
final Enumeration<URL> resources = cld.getResources(pckgname
.replace('.', '/'));
URLConnection connection;
for (URL url = null; resources.hasMoreElements()
&& ((url = resources.nextElement()) != null);) {
try {
connection = url.openConnection();
if (connection instanceof JarURLConnection) {
checkJarFile((JarURLConnection) connection, pckgname,
classes);
} else if (connection instanceof FileURLConnection) {
try {
checkDirectory(
new File(URLDecoder.decode(url.getPath(),
"UTF-8")), pckgname, classes);
} catch (final UnsupportedEncodingException ex) {
throw new ClassNotFoundException(
pckgname
+ " does not appear to be a valid package (Unsupported encoding)",
ex);
}
} else
throw new ClassNotFoundException(pckgname + " ("
+ url.getPath()
+ ") does not appear to be a valid package");
} catch (final IOException ioex) {
throw new ClassNotFoundException(
"IOException was thrown when trying to get all resources for "
+ pckgname, ioex);
}
}
} catch (final NullPointerException ex) {
throw new ClassNotFoundException(
pckgname
+ " does not appear to be a valid package (Null pointer exception)",
ex);
} catch (final IOException ioex) {
throw new ClassNotFoundException(
"IOException was thrown when trying to get all resources for "
+ pckgname, ioex);
}
return classes;
}
List<String> classNames = new FastClasspathScanner("com.mypackage").scan()
.getNamesOfAllClasses();
有许多可能的变体 - 请参阅文档(上面链接)以获取完整信息 .
4
我就是这样做的 . 我扫描所有子文件夹(子包),我不尝试加载匿名类:
/**
* Attempts to list all the classes in the specified package as determined
* by the context class loader, recursively, avoiding anonymous classes
*
* @param pckgname
* the package name to search
* @return a list of classes that exist within that package
* @throws ClassNotFoundException
* if something went wrong
*/
private static List<Class> getClassesForPackage(String pckgname) throws ClassNotFoundException {
// This will hold a list of directories matching the pckgname. There may be more than one if a package is split over multiple jars/paths
ArrayList<File> directories = new ArrayList<File>();
String packageToPath = pckgname.replace('.', '/');
try {
ClassLoader cld = Thread.currentThread().getContextClassLoader();
if (cld == null) {
throw new ClassNotFoundException("Can't get class loader.");
}
// Ask for all resources for the packageToPath
Enumeration<URL> resources = cld.getResources(packageToPath);
while (resources.hasMoreElements()) {
directories.add(new File(URLDecoder.decode(resources.nextElement().getPath(), "UTF-8")));
}
} catch (NullPointerException x) {
throw new ClassNotFoundException(pckgname + " does not appear to be a valid package (Null pointer exception)");
} catch (UnsupportedEncodingException encex) {
throw new ClassNotFoundException(pckgname + " does not appear to be a valid package (Unsupported encoding)");
} catch (IOException ioex) {
throw new ClassNotFoundException("IOException was thrown when trying to get all resources for " + pckgname);
}
ArrayList<Class> classes = new ArrayList<Class>();
// For every directoryFile identified capture all the .class files
while (!directories.isEmpty()){
File directoryFile = directories.remove(0);
if (directoryFile.exists()) {
// Get the list of the files contained in the package
File[] files = directoryFile.listFiles();
for (File file : files) {
// we are only interested in .class files
if ((file.getName().endsWith(".class")) && (!file.getName().contains("$"))) {
// removes the .class extension
int index = directoryFile.getPath().indexOf(packageToPath);
String packagePrefix = directoryFile.getPath().substring(index).replace('/', '.');;
try {
String className = packagePrefix + '.' + file.getName().substring(0, file.getName().length() - 6);
classes.add(Class.forName(className));
} catch (NoClassDefFoundError e)
{
// do nothing. this class hasn't been found by the loader, and we don't care.
}
} else if (file.isDirectory()){ // If we got to a subdirectory
directories.add(new File(file.getPath()));
}
}
} else {
throw new ClassNotFoundException(pckgname + " (" + directoryFile.getPath() + ") does not appear to be a valid package");
}
}
return classes;
}
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;
}
}
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();
//...
}
Java 8 is not a must . 您可以使用for循环而不是stream . 你可以像这样测试它
public static void main(String[] args) throws Exception {
final String pack = "java.nio.file"; // Or any other package
PackageUtil.getClasses(pack).stream().forEach(System.out::println);
}
106
值得一提
如果你想在某个包下有一个所有类的列表,你可以使用 Reflection 以下方式:
List<Class> myTypes = new ArrayList<>();
Reflections reflections = new Reflections("com.package");
for (String s : reflections.getStore().get(SubTypesScanner.class).values()) {
myTypes.add(Class.forName(s));
}
-------------------------------------------------------
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
package eric.j2se.reflect;
import java.util.Set;
import org.reflections.Reflections;
import org.reflections.scanners.ResourcesScanner;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import org.reflections.util.FilterBuilder;
/**
* an util to iterate class in a package,
*
* @author eric
* @date Dec 10, 2013 12:36:46 AM
*/
public class IteratePackageUtil {
/**
* <p>
* Get set of all class in a specified package recursively. this only support lib
* </p>
* <p>
* class of sub package will be included, inner class will be included,
* </p>
* <p>
* could load class that use the same classloader of current class, can't load system packages,
* </p>
*
* @param pkg
* path of a package
* @return
*/
public static Set<Class<? extends Object>> getClazzSet(String pkg) {
// prepare reflection, include direct subclass of Object.class
Reflections reflections = new Reflections(new ConfigurationBuilder().setScanners(new SubTypesScanner(false), new ResourcesScanner())
.setUrls(ClasspathHelper.forClassLoader(ClasspathHelper.classLoaders(new ClassLoader[0])))
.filterInputsBy(new FilterBuilder().includePackage(pkg)));
return reflections.getSubTypesOf(Object.class);
}
public static void test() {
String pkg = "org.apache.tomcat.util";
Set<Class<? extends Object>> clazzSet = getClazzSet(pkg);
for (Class<? extends Object> clazz : clazzSet) {
System.out.println(clazz.getName());
}
}
public static void main(String[] args) {
test();
}
}
23 回答
由于类加载器的动态特性,这是不可能的 . 类加载器不需要告诉VM它可以提供哪些类,而是只需要处理类的请求,并且必须返回类或抛出异常 .
但是,如果您编写自己的类加载器,或检查类路径及其 jar ,则可以找到此信息 . 这将通过文件系统操作,而不是反射 . 甚至可能有图书馆可以帮助你做到这一点 .
如果有生成或远程交付的类,您将无法发现这些类 .
相反,常规方法是在某个地方注册您需要在文件中访问的类,或者在不同的类中引用它们 . 或者只是在命名时使用约定 .
附录:The Reflections Library将允许您在当前类路径中查找类 . 它可用于获取包中的所有类:
你应该看看开源Reflections library . 有了它,您可以轻松实现您想要的 .
首先,设置反射索引(因为默认情况下禁用搜索所有类,所以它有点乱):
然后,您可以查询给定包中的所有对象:
Google Guava 14包含一个新类ClassPath,其中包含三种扫描顶级类的方法:
getTopLevelClasses()
getTopLevelClasses(String packageName)
getTopLevelClassesRecursive(String packageName)
有关详细信息,请参阅ClassPath javadocs .
您可以使用this method 1使用
ClassLoader
.1此方法最初来自http://snippets.dzone.com/posts/show/4831,该文件由Internet Archive存档,与现在相关联 . 该片段也可在https://dzone.com/articles/get-all-classes-within-package上找到 .
Spring 天
此示例适用于Spring 4,但您也可以在早期版本中找到类路径扫描程序 .
Google Guava
Note: 在版本14中,API仍标记为@Beta,因此请注意 生产环境 代码 .
你好 . 我总是遇到上述解决方案(以及其他网站)的一些问题 .
作为开发人员,我正在为API编写插件 . API允许使用任何外部库或第三方工具 . 该设置还包含jar或zip文件中的代码和直接位于某些目录中的类文件的混合 . 所以我的代码必须能够在每个设置下工作 . 经过大量研究后,我提出了一种方法,该方法至少可以在所有可能的设置中使用95% .
以下代码基本上是总是有效的过度杀伤方法 .
代码:
此代码扫描给定包中包含的所有类 . 它只适用于当前
ClassLoader
中的所有类 .这三种方法使您能够查找给定包中的所有类 .
你这样使用它:
解释:
该方法首先获得当前
ClassLoader
. 然后它获取包含所述包的所有资源并迭代这些URL
. 然后它创建一个URLConnection
并确定我们拥有什么类型的URl . 它可以是目录(FileURLConnection
),也可以是jar或zip文件中的目录(JarURLConnection
) . 根据我们有什么类型的连接,将调用两种不同的方法 .首先让我们看看如果是
FileURLConnection
会发生什么 .它首先检查传递的文件是否存在并且是否是目录 . 如果是这种情况,它会检查它是否是一个类文件 . 如果是这样,将创建
Class
对象并将其放入ArrayList
. 如果它不是类文件而是目录,我们只需迭代它并执行相同的操作 . 所有其他案例/文件将被忽略 .如果
URLConnection
是JarURLConnection
,则将调用另一个私有帮助方法 . 此方法迭代zip / jar存档中的所有条目 . 如果一个条目是类文件并且在包内,则将创建Class
对象并将其存储在ArrayList
中 .解析完所有资源后(main方法)返回
ArrayList
containsig给定包中的所有类,当前ClassLoader
知道这些类 .如果进程在任何时候都失败,将抛出
ClassNotFoundException
,其中包含有关确切原因的详细信息 .不使用任何额外的库:
通常,类加载器不允许扫描类路径上的所有类 . 但通常唯一使用的类加载器是UrlClassLoader,我们可以从中检索目录和jar文件列表(请参阅getURLs)并逐个打开它们以列出可用的类 . 这种方法称为类路径扫描,在Scannotation和Reflections中实现 .
另一种方法是使用Java Pluggable Annotation Processing API来编写注释处理器,它将在编译时收集所有带注释的类构建索引文件以供运行时使用 . 此机制在ClassIndex库中实现:
请注意,由于Java编译器会自动发现类路径中找到的任何处理器,因此扫描完全自动化无需额外设置 .
我写了FastClasspathScanner来解决这个问题 . 它处理许多不同类型的类路径扫描任务,具有简单的API,可与许多不同的ClassLoader和类路径环境一起使用,经过精心并行化,并针对高速和低内存消耗进行了高度优化 . 它甚至可以生成类图的GraphViz可视化,显示类如何相互连接 .
对于查找给定包中所有类或接口的原始问题,您可以执行以下操作:
有许多可能的变体 - 请参阅文档(上面链接)以获取完整信息 .
我就是这样做的 . 我扫描所有子文件夹(子包),我不尝试加载匿名类:
我把一个简单的github项目放在一起解决了这个问题:
https://github.com/ddopson/java-class-enumerator
它应该适用于BOTH基于文件的类路径和jar文件 .
如果在签出项目后运行'make',它将打印出来:
另见my other answer
是的,你可以使用很少的API,这就是我喜欢这样做的方式,面对这个我使用hibernate核心的问题并且必须找到带有某个注释注释的类 .
将这些作为自定义注释,使用该注释标记要拾取的类 .
然后用它来标记你的课程
使此实用程序类具有以下方法
调用 allFoundClassesAnnotatedWithEntityToBeScanned() 方法以获取找到的 Set 类 .
您将需要下面给出的库
您需要在类路径中查找每个类加载器条目:
如果entry是目录,只需在右侧子目录中查找:
如果条目是文件,并且它是jar,请检查它的ZIP条目:
现在,一旦你有了包的所有类名,你可以尝试用反射加载它们并分析它们是类还是接口等 .
我一直在尝试使用Reflections库,但是使用它时遇到了一些问题,而且我应该包含太多的jar只是为了简单地获取包上的类 .
我在这个重复的问题中找到了'll post a solution I':How to get all classes names in a package?
answer was written by sp00m;我添加了一些更正以使其工作:
要使用它,只需将find方法作为本示例中提到的sp00n调用:如果需要,我已添加了类的实例的创建 .
几乎所有答案都使用
Reflections
或从文件系统中读取类文件 . 如果您尝试从文件系统中读取类,则在将应用程序打包为JAR或其他时可能会出错 . 此外,您可能不希望为此目的使用单独的库 .这是另一种方法,它是纯java而不依赖于文件系统 .
Java 8 is not a must . 您可以使用for循环而不是stream . 你可以像这样测试它
值得一提
如果你想在某个包下有一个所有类的列表,你可以使用
Reflection
以下方式:这将创建一个类列表,以后您可以根据需要使用它们 .
Aleksander Blomskøld's solution在使用Maven时对我的参数化测试
@RunWith(Parameterized.class)
不起作用 . 测试被正确命名,也找到了但未执行的地方:据报道,类似的问题是here .
在我的情况下,
@Parameters
正在创建包中每个类的实例 . 在IDE中本地运行时,测试运行良好 . 但是,在运行Maven时,没有使用AleksanderBlomskøld的解决方案找到的类 .我确实使用了以下剪辑,其灵感来自DavidPärsson对AleksanderBlomskøld的答案的评论:
如果您没有使用任何动态类加载器,则可以搜索类路径,并为每个条目搜索目录或JAR文件 .
我刚写了一个util类,它包含测试方法,你可以检查一下〜
IteratePackageUtil.java:
这是非常可能的,但没有像
Reflections
这样的额外库,很难......这很难,因为你没有完整的仪器来获取类名 .
而且,我接受了
ClassFinder
类的代码:基于@Staale's answer,并试图不依赖第三方库,我将通过检查第一个包物理位置来实现文件系统方法:
如果您只是想加载一组相关的类,那么Spring可以帮助您 .
Spring可以在一行代码中实例化实现给定接口的所有类的列表或映射 . 列表或映射将包含实现该接口的所有类的实例 .
话虽这么说,作为从文件系统中加载类列表的替代方法,而不是实现相同的您要加载的所有类中的接口,无论使用Spring还是使用Spring为您提供所有类的实例 . 这样,您可以加载(并实例化)您想要的所有类,而不管它们是什么包 .
另一方面,如果将它们全部放在包中是您想要的,那么只需让该包中的所有类实现给定的接口即可 .
这是不可能的,因为包中的所有类可能都没有加载,而你总是知道类的包 .