Список всех подпакетов пакета

Я ищу способ перечислить все подпакеты произвольного пакета в Java.

Что-то вроде этого:

Package basePackage = getPackage("com.mypackage");
for(Package subPackage : basepackage.getSubPackages()){
  System.out.println(subPackage.getName());
}

Есть ли способ сделать это? Спасибо заранее.

Как делает IDE (скажем, Netbeans)? enter image description here

UPDATE:

Я пытаюсь найти все пакеты mappers для MyBatis. В моем проекте все пакеты mappers должны называть "*.mappers". Например: "a.b.mappers" или "a.b.c.mappers". Дело в том, что я знаю только базовый пакет и не уверен, сколько пакетов пакетов под ним.

UPDATE: Вот мой код, пытающийся использовать библиотеку отражений для этого:

private Set<String> getPackagesNames() {
    Reflections reflections = new Reflections("com.mypackage");
    Set<Class<? extends Object>> allClasses = reflections.getSubTypesOf(Object.class);
    Set<String> packageNames = new HashSet<>();

    for( Iterator<Class<? extends Object>> it = allClasses.iterator(); it.hasNext(); ) {
        Class<? extends Object> subClass= it.next();
        packageNames.add(subClass.getPackage().getName());
    }

    return packageNames;
}

Не знаю, почему это не работает. Здесь нет класса.

UPDATE

Вот мой код, чтобы сделать это. Вид медленный, но производительность не так уж важна в моем случае. Я никогда не использовал Spring раньше, поэтому, если есть лучшие способы сделать это, дайте мне знать. Спасибо.

    private static Set<String> getPackages(String basePackage) throws IOException, ClassNotFoundException {
        Set<String> packagesNames = new HashSet<>();

        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver);

        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + "/" + "**/*.class";
        Resource[] resources = resourcePatternResolver.getResources(packageSearchPath);
        for( Resource resource : resources ) {
                MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);    
                Class aClass = Class.forName(metadataReader.getClassMetadata().getClassName());
                String packageName = aClass.getPackage().getName();
                packagesNames.add(packageName);
            }
        }
        return packagesNames;
    }

    private static String resolveBasePackage(String basePackage) {
        return ClassUtils.convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(basePackage));
    }

Большая часть кода скопирована из Как читать все классы из пакета Java в пути к классам?

Ответ 1

Я полагаю, что самый простой способ получить пакеты - получить все пакеты вашего загрузчика классов с помощью

Package.getPackages()

и отфильтруйте его с именем своего пакета с помощью

packageName.startsWith("com.yourcompany.yourpackage")

Ответ 2

1) Загрузите любой класс из пакета и получите его URL

URL u = Test.class.getResource("");

2) Определите, является ли это файлом или банкой.

2) используйте File.list() для каталога или JarFile.getEntries для jar, чтобы найти подпакеты

Ответ 3

Другим возможным способом решения этой проблемы было бы использование системного загрузчика классов для загрузки всех jars/zip файлов в путь к классам, а затем просто проверить их.

Производительность не будет большой. Поскольку мы проверяем файлы jar/zip, но он работает.

Вот пример кода:

import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.TreeSet;
import java.util.jar.JarInputStream;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;

public class ClasspathLister {

    public static void main(String[] args) {
        listPackages("com/fasterxml").forEach(s -> {
            System.out.printf("%s%n", s.replace("/", ".").replaceAll("\\.$", ""));
        });
    }

    private static Set<String> listPackages(String prefix) {
        URLClassLoader sysloader = (URLClassLoader) ClassLoader.getSystemClassLoader();
        return Arrays.stream(sysloader.getURLs())
            .filter(u -> u.toString().matches("(?i).+(\\.jar|\\.zip)$"))
            .flatMap(u -> listJar(u, prefix).stream())
            .collect(Collectors.toCollection(TreeSet::new));
    }

    private static Set<String> listJar(URL u, String prefix) {
        Set<String> packages = new LinkedHashSet<>();
        try (JarInputStream in = 
            new JarInputStream(Files.newInputStream(Paths.get(u.toURI())))) {
            ZipEntry ze;
            while ((ze = in.getNextEntry()) != null) {
                if (ze.isDirectory() && ze.getName().startsWith(prefix)) {
                    packages.add(ze.getName());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return packages;
    }
}

Это приведет к тому, что:

com.fasterxml
com.fasterxml.jackson
com.fasterxml.jackson.annotation
com.fasterxml.jackson.core
com.fasterxml.jackson.core.base
com.fasterxml.jackson.core.filter
com.fasterxml.jackson.core.format
com.fasterxml.jackson.core.io
com.fasterxml.jackson.core.json
com.fasterxml.jackson.core.sym
com.fasterxml.jackson.core.type
com.fasterxml.jackson.core.util
com.fasterxml.jackson.databind
com.fasterxml.jackson.databind.annotation
com.fasterxml.jackson.databind.cfg
com.fasterxml.jackson.databind.deser
com.fasterxml.jackson.databind.deser.impl
com.fasterxml.jackson.databind.deser.std
com.fasterxml.jackson.databind.exc
...

Ответ 4

Чтобы разработать: IDE фактически читает файлы JAR зависимостей. В JAR файле это просто структура каталогов. Это отличается от способа хранения Java ClassLoader.