Получение всех Java классов из ClassPath

Уже не первый раз наталкиваюсь на достаточно общую задачу: найти все классы в classpath приложения. Каждый раз гуглю, пишу код, а потом он где-то теряется. Поэтому, выкладываю код своего решения, может быть кому-нибудь пригодится. А если не пригодится напрямую, здесь видно как читать Jar-файлы))) Короче, говоря, гугл рассудит) Краткое описание подхода, чтобы лучше понимался код:
  • Разделяем строку classpath на записи, ссылающиеся на конкретные директории и jar-файлы. В линуксе разделитель между ними - двоеточие, в винде - точка с запятой. Дальше, для каждой записи:
  • Если запись ссылается на директорию - рекурсивно сканируем ее, считая директорию, откуда начали сканирование соответствующей корневому пакету.
  • Если запись ссылается на файл, пытаемся анализировать его как Jar-файл, используя инструментарий из пакета java.util.jar
Чтобы использовать нижеприведенный класс необходимо получить саму строку classpath. Это осуществляется простым вызовом System.getProperty ("java.class.path")
Итак, код:

package ru.infoconnect.utils.classes;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class ClassPathScanner {

	/**
	 * Просканировать строку, представляющую из себя classpath и вернуть
	 * коллекцию найденных классов
	 * 
	 * @param cp
	 *            classpath
	 * @return коллекция обнаруженных классов
	 * @throws IOException
	 */
	public static Collection<String> scan( String cp ) throws IOException {
		Collection<String> classes = new ArrayList<String>();
		scan( cp, classes );
		return classes;
	}

	/**
	 * Просканировать строку, представляющую из себя classpath и добавить все
	 * найденные классы в коллекцию
	 * 
	 * @param cp
	 *            classpath
	 * @param classes
	 *            коллекция классов, в которую добавляются результаты
	 * @throws IOException
	 */
	public static void scan( String cp, Collection<String> classes ) throws IOException {
		String[] entries = cp.split( File.pathSeparator );

		for ( String entryName : entries ) {
			File file = new File( entryName );
			if ( file.isDirectory() ) {
				scanDir( "", file, classes );
			} else if ( file.getName().endsWith( ".jar" ) || file.getName().endsWith( ".zip" ) ) {
				scanJar( file, classes );
			} else {
				throw new IOException( "Unknown classpath entry " + file.getName() );
			}
		}

	}

	/**
	 * Сканировать Jar-файл на предмет наличия class-файлов
	 * 
	 * @param jarFile
	 *            файл архива
	 * @param classes
	 *            коллекция классов, в которую добавляются результаты
	 * @throws IOException
	 */
	private static void scanJar( File jarFile, Collection<String> classes ) throws IOException {
		JarFile jar = new JarFile( jarFile ); // Открываем Jar-файл для чтения списка файлов

		for ( Enumeration<JarEntry> e = jar.entries(); e.hasMoreElements(); ) {
			JarEntry entry = e.nextElement();

			if ( entry.isDirectory() ) // Попускаем директории
				continue;

			scanFileName( entry.getName(), classes ); // Если элемент - файл, проверяем его имя
		}

		jar.close();
	}

	/**
	 * Сканировать директорию, представляющую пакет на предмет наличия
	 * class-файлов.
	 * 
	 * @param pkg
	 *            имя пакета
	 * @param dir
	 *            директория
	 * @param classes
	 *            коллекция классов, в которую добавляются результаты
	 */
	private static void scanDir( String pkg, File dir, Collection<String> classes ) {
		for ( File file : dir.listFiles() ) { // Перебираем все файлы в директории
			if ( file.isDirectory() ) {
				scanDir( pkg + file.getName() + File.separator, file, classes ); // Сканируем директорию пакета
			} else {
				scanFileName( pkg + file.getName(), classes ); // Проверяем имя файла
			}
		}
	}

	/**
	 * Проверить имя файла и извлечь имя класса
	 * 
	 * @param name
	 *            имя файла
	 * @param classes
	 *            коллекция классов, в которую добавляются результаты
	 */
	private static void scanFileName( String name, Collection<String> classes ) {
		if ( !name.endsWith( ".class" ) )
			return;

		classes.add( name.substring( 0, name.length() - 6 ).replace( File.separator, '.' ) ); // Извлекаем имя класса из имени файла: удаляем расширение и преобразуем разделители
	}
}

Ссылки

Как, обычно, несколько ссылок по теме:

 Подписаться на RSS

 #  #  #  #  #  #  #  #  #  #

Комментарии

  1. Знакомая техника. Иногда без этого обойтись трудно. Например, во всякого рода загрузчиках,
    которые ищут все классы, реализующие "точку-входа".

  2. 1. шкурный вопрос - лицензия? :) (я понимаю что код простой, но переписывать лень) Вышел на код из гугла, посему не уверен - может она где и указана...

    2. Java 1.7: File.separator -> File.pathSeparatorChar

  3. 1. Public Domain
    2. Тогда уж так:
    File.separator -> File.separatorChar
    File.pathSeparator -> File.pathSeparatorChar

    И разве в Java 1.7 соответствующие строковые константы убрали? Пока с ней не сталкивался просто...

  4. Спасибо огромное! Очень помогли!

Добавить комментарий