Получение всех Классов от Пакета

Хотя вышеупомянутые ответы довольно корректны, я думаю, что намного более важно отметить, что слова "шаблон разработки" абсолютно неизвестны 90% всех людей, которые создают программное обеспечение. Они только начинают писать код.

проблема не выбирает лучший подход дизайна, она убеждает других, что дизайн имеет значение.

11
задан JustMaximumPower 27 November 2009 в 21:02
поделиться

7 ответов

Вот базовый пример, предполагая, что классы не упакованы в JAR:

// Prepare.
String packageName = "com.example.commands";
List<Class<ICommand>> commands = new ArrayList<Class<ICommand>>();
URL root = Thread.currentThread().getContextClassLoader().getResource(packageName.replace(".", "/"));

// Filter .class files.
File[] files = new File(root.getFile()).listFiles(new FilenameFilter() {
    public boolean accept(File dir, String name) {
        return name.endsWith(".class");
    }
});

// Find classes implementing ICommand.
for (File file : files) {
    String className = file.getName().replaceAll(".class$", "");
    Class<?> cls = Class.forName(packageName + "." + className);
    if (ICommand.class.isAssignableFrom(cls)) {
        commands.add((Class<ICommand>) cls);
    }
}
15
ответ дан 3 December 2019 в 03:35
поделиться

Вот служебный метод, использующий Spring.

Подробности о шаблоне можно найти здесь

    public static List<Class> listMatchingClasses(String matchPattern) throws IOException {
    List<Class> classes = new LinkedList<Class>();
    PathMatchingResourcePatternResolver scanner = new PathMatchingResourcePatternResolver();
    Resource[] resources = scanner.getResources(matchPattern);

    for (Resource resource : resources) {
        Class<?> clazz = getClassFromResource(resource);
        classes.add(clazz);
    }

    return classes;
}



public static Class getClassFromResource(Resource resource) {
    try {
        String resourceUri = resource.getURI().toString();
        resourceUri = resourceUri.replace(esourceUri.indexOf(".class"), "").replace("/", ".");
        // try printing the resourceUri before calling forName, to see if it is OK.
        return Class.forName(resourceUri);
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    return null;
}
3
ответ дан 3 December 2019 в 03:35
поделиться

Да, но это не самая простая задача. С этим есть много проблем. Не все классы легко найти. Некоторые классы могут находиться в: Jar, как файл класса, по сети и т. Д.

Взгляните на этот поток.

Чтобы убедиться, что они принадлежат к типу ICommand, вам придется использовать отражение чтобы проверить наследующий класс.

1
ответ дан 3 December 2019 в 03:35
поделиться

Ниже, реализация с использованием JSR-199 API, т.е. классы из javax.tools. * :

List<Class> commands = new ArrayList<Class>();

JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(
        null, null, null);

Location location = StandardLocation.CLASS_PATH;
String packageName = "commands";
Set<JavaFileObject.Kind> kinds = new HashSet<JavaFileObject.Kind>();
kinds.add(JavaFileObject.Kind.CLASS);
boolean recurse = false;

Iterable<JavaFileObject> list = fileManager.list(location, packageName,
        kinds, recurse);

for (JavaFileObject javaFileObject : list) {
    commands.add(javaFileObject.getClass());
}
6
ответ дан 3 December 2019 в 03:35
поделиться

Начните с общедоступного Classloader.getResources (имя строки). Спросите у загрузчика классов класс, соответствующий каждому имени в интересующем вас пакете. Повторите для всех релевантных загрузчиков классов.

1
ответ дан 3 December 2019 в 03:35
поделиться

Это был бы очень полезный инструмент, который нам нужен, и JDK должен предоставить некоторую поддержку.

Но, наверное, лучше сделать это во время сборки. Вы знаете, где находятся все ваши файлы классов, и можете проверять их статически и строить график. Во время выполнения вы можете запросить этот график, чтобы получить все подтипы. Это требует дополнительной работы, но я считаю, что это действительно относится к процессу сборки.

1
ответ дан 3 December 2019 в 03:35
поделиться

Используя ClasspathSuite Йоханнеса Линка , я смог сделать это следующим образом:

import org.junit.extensions.cpsuite.ClassTester;
import org.junit.extensions.cpsuite.ClasspathClassesFinder;

public static List<Class<?>> getClasses(final Package pkg, final boolean includeChildPackages) {
    return new ClasspathClassesFinder(new ClassTester() {
        @Override public boolean searchInJars() { return true; }
        @Override public boolean acceptInnerClass() { return false; }
        @Override public boolean acceptClassName(String name) {
            return name.startsWith(pkg.getName()) && (includeChildPackages || name.indexOf(".", pkg.getName().length()) != -1);
        }
        @Override public boolean acceptClass(Class<?> c) { return true; }
    }, System.getProperty("java.class.path")).find();
}

ClasspathClassesFinder ищет файлы классов и jar-файлы в системном пути к классам.

В вашем конкретном случае, вы можете изменить acceptClass следующим образом:

@Override public boolean acceptClass(Class<?> c) {
    return ICommand.class.isAssignableFrom(c);
}

Следует отметить одно: будьте осторожны с тем, что вы возвращаете в acceptClassName, поскольку следующее, что делает ClasspathClassesFinder, - это загружает класс и вызывает acceptClass. Если acceptClassName всегда возвращает true, вы в конечном итоге загрузите каждый класс в пути к классам, и это может вызвать OutOfMemoryError.

0
ответ дан 3 December 2019 в 03:35
поделиться
Другие вопросы по тегам:

Похожие вопросы: