[java] 리플렉션을 사용하여 패키지의 모든 클래스를 찾을 수 있습니까?

주어진 패키지에서 모든 클래스 또는 인터페이스를 찾을 수 있습니까? (예를 들어 빠르게 살펴보면 Package아니오처럼 보일 것입니다.)



답변

클래스 로더의 동적 특성으로 인해 불가능합니다. 클래스 로더는 VM에 제공 할 수있는 클래스를 알려줄 필요가 없으며 대신 클래스에 대한 요청을 처리 한 것이므로 클래스를 반환하거나 예외를 발생시켜야합니다.

그러나 클래스 로더를 직접 작성하거나 클래스 경로와 항아리를 검사하면이 정보를 찾을 수 있습니다. 이것은 반영이 아닌 파일 시스템 작업을 통해 이루어집니다. 이를 수행하는 데 도움이되는 라이브러리가있을 수도 있습니다.

생성되거나 원격으로 제공되는 클래스가 있으면 해당 클래스를 발견 할 수 없습니다.

일반적인 방법은 액세스 할 클래스를 파일에 등록하거나 다른 클래스에서 참조하는 것입니다. 또는 명명과 관련하여 규칙을 사용하십시오.

부록 : 리플렉션 라이브러리 를 사용하면 현재 클래스 경로에서 클래스를 찾을 수 있습니다. 패키지의 모든 클래스를 가져 오는 데 사용할 수 있습니다.

 Reflections reflections = new Reflections("my.project.prefix");

 Set<Class<? extends Object>> allClasses =
     reflections.getSubTypesOf(Object.class);


답변

아마도 오픈 소스 리플렉션 라이브러리를 살펴보아야 할 것입니다 . 그것으로 당신은 당신이 원하는 것을 쉽게 달성 할 수 있습니다.

먼저 리플렉션 인덱스를 설정하십시오 (기본적으로 모든 클래스 검색이 비활성화되어 있기 때문에 약간 지저분합니다).

List<ClassLoader> classLoadersList = new LinkedList<ClassLoader>();
classLoadersList.add(ClasspathHelper.contextClassLoader());
classLoadersList.add(ClasspathHelper.staticClassLoader());

Reflections reflections = new Reflections(new ConfigurationBuilder()
    .setScanners(new SubTypesScanner(false /* don't exclude Object.class */), new ResourcesScanner())
    .setUrls(ClasspathHelper.forClassLoader(classLoadersList.toArray(new ClassLoader[0])))
    .filterInputsBy(new FilterBuilder().include(FilterBuilder.prefix("org.your.package"))));

그런 다음 주어진 패키지의 모든 객체를 쿼리 할 수 ​​있습니다.

Set<Class<?>> classes = reflections.getSubTypesOf(Object.class);


답변

Google Guava 14에는 ClassPath최상위 클래스를 스캔하는 세 가지 방법이 있는 새로운 클래스 가 포함되어 있습니다.

  • getTopLevelClasses()
  • getTopLevelClasses(String packageName)
  • getTopLevelClassesRecursive(String packageName)

자세한 정보 는 ClassPathjavadocs 를 참조하십시오.


답변

를 사용하는 이 방법 1 을 사용할 수 있습니다 ClassLoader.

/**
 * Scans all classes accessible from the context class loader which belong to the given package and subpackages.
 *
 * @param packageName The base package
 * @return The classes
 * @throws ClassNotFoundException
 * @throws IOException
 */
private static Class[] getClasses(String packageName)
        throws ClassNotFoundException, IOException {
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    assert classLoader != null;
    String path = packageName.replace('.', '/');
    Enumeration<URL> resources = classLoader.getResources(path);
    List<File> dirs = new ArrayList<File>();
    while (resources.hasMoreElements()) {
        URL resource = resources.nextElement();
        dirs.add(new File(resource.getFile()));
    }
    ArrayList<Class> classes = new ArrayList<Class>();
    for (File directory : dirs) {
        classes.addAll(findClasses(directory, packageName));
    }
    return classes.toArray(new Class[classes.size()]);
}

/**
 * Recursive method used to find all classes in a given directory and subdirs.
 *
 * @param directory   The base directory
 * @param packageName The package name for classes found inside the base directory
 * @return The classes
 * @throws ClassNotFoundException
 */
private static List<Class> findClasses(File directory, String packageName) throws ClassNotFoundException {
    List<Class> classes = new ArrayList<Class>();
    if (!directory.exists()) {
        return classes;
    }
    File[] files = directory.listFiles();
    for (File file : files) {
        if (file.isDirectory()) {
            assert !file.getName().contains(".");
            classes.addAll(findClasses(file, packageName + "." + file.getName()));
        } else if (file.getName().endsWith(".class")) {
            classes.add(Class.forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6)));
        }
    }
    return classes;
}

__________
1 이 방법은 원래부터 찍은 http://snippets.dzone.com/posts/show/4831 된, 보관 지금 연결로 인터넷 아카이브에 의해. 스 니펫은 https://dzone.com/articles/get-all-classes-within-package 에서 사용할 수 있습니다 .


답변

이 예제는 Spring 4에 대한 것이지만 이전 버전에서도 클래스 경로 스캐너를 찾을 수 있습니다.

// 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 ...
}

구글 구아바

참고 : 버전 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
  }
}


답변

안녕하세요. 위의 솔루션과 다른 사이트에서 항상 문제가있었습니다.
개발자로서 API 용 애드온을 프로그래밍하고 있습니다. API는 외부 라이브러리 또는 타사 도구를 사용하지 못하게합니다. 설정은 jar 또는 zip 파일의 코드와 일부 디렉토리에 직접 위치한 클래스 파일로 구성됩니다. 그래서 내 코드는 모든 설정에서 arround를 사용할 수 있어야했습니다. 많은 연구 끝에 나는 가능한 모든 설정의 적어도 95 %에서 작동하는 방법을 생각해 냈습니다.

다음 코드는 기본적으로 항상 작동하는 과도한 방법입니다.

코드:

이 코드는 포함 된 모든 클래스에 대해 주어진 패키지를 스캔합니다. 현재의 모든 클래스에서만 작동합니다 ClassLoader.

/**
 * 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;
}

이 세 가지 방법은 지정된 패키지에서 모든 클래스를 찾을 수있는 기능을 제공합니다.
당신은 이것을 다음과 같이 사용합니다 :

getClassesForPackage("package.your.classes.are.in");

설명:

이 메소드는 먼저 current를 가져옵니다 ClassLoader. 그런 다음 해당 패키지가 포함 된 모든 리소스를 가져오고이 패키지를 반복 URL합니다. 그런 다음 a를 만들고 URLConnection어떤 유형의 URl이 있는지 결정합니다. 디렉토리 ( FileURLConnection) 또는 jar 또는 zip 파일 ( JarURLConnection) 내의 디렉토리 일 수 있습니다 . 연결 유형에 따라 두 가지 다른 메소드가 호출됩니다.

먼저이면 어떻게되는지 봅시다 FileURLConnection.
먼저 전달 된 File이 존재하고 디렉토리인지 확인합니다. 이 경우 클래스 파일인지 확인합니다. 그렇다면 Class객체가 생성되어에 삽입됩니다 ArrayList. 클래스 파일이 아니라 디렉토리 인 경우 간단히 반복하여 동일한 작업을 수행합니다. 다른 모든 사례 / 파일은 무시됩니다.

(가) 경우 URLConnectionA는 JarURLConnection다른 개인 도우미 메서드가 호출됩니다. 이 방법은 zip / jar 아카이브의 모든 항목을 반복합니다. 하나의 항목이 클래스 파일이고 패키지 내부에 있으면 Class객체가 생성되어에 저장됩니다 ArrayList.

모든 리소스를 파싱 한 후 (메인 메서드) ArrayList현재 지정된 패키지의 모든 클래스를 포함하는 클래스를 반환합니다 ClassLoader.

프로세스가 어느 시점에서 실패 ClassNotFoundException하면 정확한 원인에 대한 자세한 정보가 포함 된 a 가 발생합니다.


답변

추가 라이브러리를 사용하지 않고 :

package test;

import java.io.DataInputStream;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

public class Test {
    public static void main(String[] args) throws Exception{
        List<Class> classes = getClasses(Test.class.getClassLoader(),"test");
        for(Class c:classes){
            System.out.println("Class: "+c);
        }
    }

    public static List<Class> getClasses(ClassLoader cl,String pack) throws Exception{

        String dottedPackage = pack.replaceAll("[/]", ".");
        List<Class> classes = new ArrayList<Class>();
        URL upackage = cl.getResource(pack);

        DataInputStream dis = new DataInputStream((InputStream) upackage.getContent());
        String line = null;
        while ((line = dis.readLine()) != null) {
            if(line.endsWith(".class")) {
               classes.add(Class.forName(dottedPackage+"."+line.substring(0,line.lastIndexOf('.'))));
            }
        }
        return classes;
    }
}