Загрузка классов из набора файлов

У меня есть задача проверить набор условий для неизвестного набора классов из пути к классам. Я хочу просканировать его на наличие классов, загрузить каждый из них и выполнить свои проверки. Теперь у меня есть набор URL-адресов для файлов классов, и я пытаюсь использовать URLClassLoader. Но для загрузки класса мне нужно указать полное имя класса, но у меня их нет (у меня есть только путь к файлу). Я не думаю, что создание имени класса из пути к файлу класса является надежным, это лучший способ сделать это?

Спасибо!


person dbf    schedule 02.07.2015    source источник
comment
Почему построение имени класса из пути к файлу класса ненадежно? Я думаю, что это. Единственная проблема, которую я вижу, это внутренние классы (несколько классов в одном файле класса). Это проблема.   -  person Aleksandar    schedule 02.07.2015
comment
У меня есть что-то вроде C:\Projects\my_project_trunk\module\target\classes\com\a\b\c\d\e\f\Smth.class на моем рабочем столе Win и что-то вроде /home/user/teamcity/agent1/ .../a/b/c/d/e/f/Smth.class на сервере сборки, поэтому мой парсер должен найти часть «классы», имя пакета сборки и т. д. Интересно, почему мы не можем получить имя класса из файла класса, оно должно быть там...   -  person dbf    schedule 02.07.2015
comment
Я бы просто проанализировал начало файла класса, ища ключевое слово пакета и первое вхождение ключевого слова класса. Затем, когда вы объедините эти два (packageName + . + className), это должно привести к правильному имени класса.   -  person PiotrSliwa    schedule 02.07.2015
comment
@PiotrSliwa Сделайте это ответом (для голосования). Ты прав.   -  person Aleksandar    schedule 02.07.2015


Ответы (2)


Я бы просто проанализировал начало файла класса, ища ключевое слово «пакет» и первое вхождение ключевого слова «класс». Затем, когда вы объедините эти два (packageName + "." + className), это должно привести к правильному имени класса.

person PiotrSliwa    schedule 02.07.2015
comment
PiotrSliwa, спасибо за идею. Наконец, я использовал библиотеку commons-bcel для разбора файла .class, что немного проще. - person dbf; 03.07.2015

Однажды я начал проект, чтобы автоматически тестировать классы, найденные на пути к классу, на наличие исключений во время выполнения, вызывая конструкторы и методы с помощью хитрых аргументов, таких как null, 0, 1, -1, "" и т. д.

В этом проекте есть класс с именем Finder, который делает примерно то, что вам нужно:

    static List<Class<?>> findClassesForPackage(String packagename, Report report) throws ClassNotFoundException {
            // 'classes' will hold a list of directories matching the package name.
            // There may be more than one if a package is split over multiple
            // jars/paths
            List<Class<?>> classes = new ArrayList<Class<?>>();
            List<File> directories = new ArrayList<File>();
            try {
                    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
                    if (classLoader == null) {
                            throw new ClassNotFoundException("Can't get class loader.");
                    }
                    // Ask for all resources for the path
                    String path = packagename.replace('.', '/');
                    Enumeration<URL> resources = classLoader.getResources(path);
                    while (resources.hasMoreElements()) {
                            URL res = resources.nextElement();
                            if (res.getProtocol().equalsIgnoreCase("jar")) {
                                    JarURLConnection conn = (JarURLConnection) res.openConnection();
                                    JarFile jar = conn.getJarFile();
                                    for (JarEntry entry : Collections.list(jar.entries())) {
                                            if (entry.getName().startsWith(path) && entry.getName().endsWith(".class")
                                                            && !entry.getName().contains("$")) {
                                                    String className = entry.getName().replace("/", ".").substring(0,
                                                                    entry.getName().length() - 6);
                                                    LOG.debug("Adding JAR className " + className);
                                                    try {
                                                            Class<?> clazz = Class.forName(className);
                                                            classes.add(clazz);
                                                            report.addClass(className);
                                                    } catch (Throwable throwable) {
                                                            ParamSet params = new ParamSet();
                                                            params.addParamValue(new ParamValue(className, "fully qualified classname"));
                                                            report.addError(className, new Error("Class.forName()", params, throwable));
                                                    }
                                            }
                                    }
                            } else
                                    directories.add(new File(URLDecoder.decode(res.getPath(), "UTF-8")));
                    }
            } catch (NullPointerException e) {
                    throw new ClassNotFoundException(String.format("%s does not appear to be a valid package", packagename), e);
            } catch (UnsupportedEncodingException e) {
                    throw new ClassNotFoundException(String.format("%s does not appear to be a valid package", packagename), e);
            } catch (IOException e) {
                    throw new ClassNotFoundException(String.format("Could not get all resources for %s", packagename), e);
            }
            List<String> subPackages = new ArrayList<String>();
            // For every directory identified capture all the .class files
            for (File directory : directories) {
                    if (directory.exists()) {
                            // Get the list of the files contained in the package
                            File[] files = directory.listFiles();
                            for (File file : files) {
                                    // add .class files to results
                                    String fileName = file.getName();
                                    if (file.isFile() && fileName.endsWith(".class")) {
                                            // removes the .class extension
                                            String className = packagename + '.' + fileName.substring(0, fileName.length() - 6);
                                            LOG.debug("Adding FILE className " + className);
                                            try {
                                                    Class<?> clazz = Class.forName(className);
                                                    classes.add(clazz);
                                                    report.addClass(className);
                                            } catch (Throwable throwable) {
                                                    ParamSet params = new ParamSet();
                                                    params.addParamValue(new ParamValue(className, "fully qualified classname"));
                                                    report.addError(className, new Error("Class.forName()", params, throwable));
                                            }
                                    }
                                    // keep track of subdirectories
                                    if (file.isDirectory()) {
                                            subPackages.add(packagename + "." + fileName);
                                    }
                            }
                    } else {
                            throw new ClassNotFoundException(String.format("%s (%s) does not appear to be a valid package",
                                            packagename, directory.getPath()));
                    }
            }
            // check all potential subpackages
            for (String subPackage : subPackages) {
                    classes.addAll(findClassesForPackage(subPackage, report));
            }
            return classes;
    }

Вам, вероятно, придется удалить некоторый код, который делает отчеты и т.д.

person Adriaan Koster    schedule 02.07.2015