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


527

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


2
참고로 클래스 경로에 공백 문자가 있으면 (그리고 아마도 영숫자가 아닌 다른 문자의 경우) 버그가 있지만 솔루션 Amit이 작동합니다. 모든 종류의 프로덕션 코드에서 사용하는 경우 해결 방법에 대한 그의 답변에 대한 내 의견을 참조하십시오.
Kip

2
이 게시물 에도 주목 하십시오 .
barfuin

1
관련 답변 참조 : stackoverflow.com/a/30149061/4102160
Cfx

1
이 게시물 에도 주목 하십시오 .
sp00m

1
ClassGraph에 대한 아래 답변을 참조하십시오. 현재 클래스 경로 및 모듈 경로를 스캔하는 가장 강력한 방법입니다.
Luke Hutchison

답변:


373

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

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

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

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

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

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

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

12
클래스 이름을 쿼리 할 수 ​​없기 때문에 오랫동안 버그가 발생했습니다. 물론 어렵고 성능이 크게 다를 수 있으며 특정 클래스 로더의 경우 목록이 정의되지 않았거나 제한이 없지만 해결할 수있는 방법이 있습니다.
Mr. Shiny and New 安 宇

16
이 솔루션은 기본적으로 getSubTypesOf가 Object의 하위 유형을 리턴하지 않으므로 작동하지 않습니다. SubTypeScanner를 구성하는 방법은 Aleksander Blomskøld의 솔루션을 참조하십시오.
Alex Spurling

17
반사에는 구아바가 필요합니다. 구아바는 크다. 버전 14.0.1은 2.1MB입니다.
mike jones

3
나를 위해 일하지 않았다. Mac OSX-Reflections 종속성 버전 0.9.9-RC1 (maven)-JDK 1.7. 허용 된 답변을 다시 고려하십시오. @ AleksanderBlomskøld 답변은 갈 것입니다 !!!!!
Konstantinos Margaritis

68
빈 목록이 반환되면 다음과 같이 Reflections 객체를 초기화하십시오. Reflections reflections = new Reflections ( "your.package.here", new SubTypesScanner (false));
João Rocha da Silva

186

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

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

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);

6
아, 여기에 우리가 간다 : code.google.com/p/reflections/issues/detail?id=122를 . 기본적으로 개체는 제외되지만 개체를 ​​다시 트리거 할 수 있습니다. 이 라이브러리를 알려 주셔서 감사합니다.
mtrc

1
이 코드 (기본 라이브러리 관련)로 Mac의 문제가 발생했지만 .addUrls(ClasspathHelper.forJavaClassPath())위의 대신 사용 하면 문제가 해결되었습니다. 적은 코드도!
David Pärsson

3
기본 패키지를 얻는 가장 간단한 방법이 궁금하다면 접두사가 빈 문자열-> ""이되는 것입니다.
JBA

2
"Reflections"라이브러리에는 까다로운 라이센스 ( github.com/ronmamo/reflections/blob/master/COPYING.txt)가 있습니다. 비결은 라이센스 자체 만 라이센스를 자유롭게 사용할 수 있다는 것입니다. 따라서 라이센스가 아닌 라이브러리를 실제로 사용하려면 모든 사람이 저자에게 연락하여 사용 약관을 협상해야합니다.
Serge Rogatch

3
STF, WTFPL 을 잘못 이해한다고 생각합니다 : wtfpl.net WTFPL 은 라이센스뿐 아니라 코드를 사용하여 원하는 모든 것을 자유롭게 할 수 있다고 생각합니다.
Richo

122

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

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

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


이것은 리플렉션이 할 수없는 곳에서 일했습니다 (공통적 인 직접적인 조상, 공통적 인 주석 없음)
Riccardo Cossu

1
아래의 코멘트 에서 언급했듯이 , ClassPath태그가 붙어 @Beta있으므로 어떤 사람들에게는 좋은 생각이 아닐 수도 있습니다.
Christian

1
리플렉션이 수행되지 않는 곳에서 이것이 효과가 있다고 말하는 것은 약간 이상하지만, 리플렉션 (및 클래스 로더) 기능을 사용하여 솔루션을 구현합니다.
Maarten Bodewes

5
나는 그가 다른 답변에서 언급 한 Reflections 라이브러리를 의미한다고 생각합니다.
Christoph Leiter

구아바 버전 28.1-jre를 사용하는 경우 Java 11에서 작동합니다.
gorjanz

111

를 사용하는 이 방법 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 에서 사용할 수 있습니다 .


6
경로에 공백이 있으면 문제가있었습니다. URL 클래스는 공백을 이스케이프 %20했지만 new File()생성자는이 문자를 리터럴 백분율 부호 2 0으로 처리했습니다. dirs.add(...)이 줄을 다음과 같이 변경하여 수정했습니다 . dirs.add(new File(resource.toURI())); 이것은 또한URISyntaxExceptiongetClasses
Kip

19
dzone.com/articles/get-all-classes-within-package 에서 방금 복사 했습니다 ! 다음 번에 소스를 참조하십시오
RS

19
이 솔루션에는 외부 라이브러리가 필요하지 않기 때문에 +1 ... 절대로 절대로 코드를 무작위로 결합하여 결코 이와 같은 작은 것을 달성하지 마십시오. 공격자에게 잠재적 인 공격 영역을 추가하고 있다는 것을 알고 있습니까? 2015 년 11 월 Jboss / Weblogic에 배포 된 앱의 클래스 경로에 Apache Commons가 있으면 원격 명령 실행으로 이어질 Apache Commons 문제가 발견되었습니다. [ foxglovesecurity.com/2015/11/06/…
sc0p

@Qix는이 코드가 jar을 지원하지 않는다고 올바르게 지적했습니다. jar 및 디렉토리 를 지원하기 위해 . 코드는 다음과 같이 변경되었습니다.
user1523177

1
좋은 해결책이지만 'Class.forName (String className)'을 'Class.forName (String className, boolean initialize, ClassLoader loader)'로 바꾸면 더 좋을 것 같습니다. 여기서 'initialize = false;' 클래스 인스턴스를 만들지 않기 위해.
Andrei_N

99

이 예제는 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
  }
}

5
훌륭한 답변입니다. 여기에 상세하고 테스트되지 않았으며 작동하지 않는 솔루션이 너무 많습니다! 이것은 환상적입니다 : 간결하고 테스트되었습니다 (구아바 출신). 아주 좋아요! 유용하며 더 많은 투표를 받아야합니다.
JeanValjean

불행히도 ClassPath구아바 의 클래스에는 다음과 @Beta같이 표시됩니다 . "클래스 또는 메소드 레벨에서 @Beta 주석으로 표시된 API는 변경 될 수 있습니다. 어떤 방식 으로든 수정하거나 모든 주요 릴리스에서 제거 할 수 있습니다. 라이브러리 자체입니다 (예 : 자신의 통제 범위를 벗어난 사용자의 CLASSPATH에 사용됨). 다시 패키지하지 않는 한 베타 API를 사용해서는 안됩니다 ... " code.google.com/p/guava-libraries/#Important_Warnings
Christian

@Christian 좋은 지적, 나는 눈치 채지 못했다! 감사합니다. 베타 클래스가 아닌 Spring 클래스 패스 스캐너를 사용하여 다른 답변을 추가 할 것입니다.
voho

구아바 솔루션을 사용하여 중첩 정적 클래스를 찾으려면 getAllClasses()메소드를 사용할 수 있습니다.
v.ladynev

1
Spring 솔루션은 실행 가능한 jar에서 실행되는 경우에만 작동합니다.
Luke

38

안녕하세요. 위의 솔루션과 다른 사이트에서 항상 문제가있었습니다.
개발자로서 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 가 발생합니다.


4
이 예에서는 가져 오기가 필요한 것으로 보이며 sun.net.www.protocol.file.FileURLConnection컴파일시 경고를 생성합니다 ( "경고 : sun.net.www.protocol.file.FileURLConnection은 Sun 독점 API이며 향후 릴리스에서 제거 될 수 있습니다"). 해당 클래스를 사용하는 대안이 있습니까, 아니면 주석을 사용하여 경고를 억제 할 수 있습니까?
Christian

이 메소드는 java.lang, java.util의 부트 스트랩 클래스에서는 작동하지 않습니다. System.getProperty ( "sun.boot.class.path")를 다음과 같이 분리하여 찾을 수 있습니다. (OS에 따라 다름) 위의 checkDirectory 및 checkJarFile의 약간 수정 된 버전을 실행합니다.
coderforlife 23시 33 분

1
connection.getClass (). getCanonicalName (). equals ( "sun.net.www.protocol.file.FileURLConnection")을 사용하여 경고 / 오류를 해결할 수 있습니다. 정말로 원한다면 sun.net.www.protocol.file.FileURLConnection을 사용하고 연결 클래스의 이름과 사용자가 만든 클래스의 이름을 비교해야하는 URLConnection을 만들 수 있습니다. 둘 다 동일하면 클래스 이름이 변경되는 경우 실패하지 않고 sun.net.www.protocol.file.FileURLConnection의 인스턴스로 취급 할 수 있습니다.
William Deans

1
@Christian FileURLConnection은 다음과 같은 일을 피할 수 있습니다. if ( ... instanceof JarURLConnecton) { ... } else { // Asume that the Connection is valid and points to a File }JPA 어노테이션이있는 클래스를 검색하기 위해 코드에서 수행 한 작업
Zardoz89

15

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

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

나는 JAR에서이 프로그램을 실행할 때 upackage입니다 null... :(
기독교

"com.mycompany.beans"패키지의 경우 "test"를 "com / mycompany / beans"로
바꾸십시오.

4
이 코드를 사용할 때 null이 발생합니다. jar 파일이 실행 가능한 경우에만 작동하는 것 같습니다
Alao

에서 패키지 이름을 얻은 경우 String pack = getPackage().getName();다음을 추가해야합니다.pack = pack.replaceAll("[.]", "/");
user2682877

13

일반적으로 클래스 로더는 클래스 경로의 모든 클래스를 스캔 할 수 없습니다. 그러나 일반적으로 유일하게 사용되는 클래스 로더는 디렉토리 및 jar 파일 목록을 검색하고 ( getURLs 참조 ) 사용 가능한 클래스를 나열하기 위해 하나씩 열 수있는 UrlClassLoader입니다 . 클래스 경로 스캔이라고하는이 방법은 Scannotation and Reflections 에서 구현됩니다 .

Reflections reflections = new Reflections("my.package");
Set<Class<? extends Object>> classes = reflections.getSubTypesOf(Object.class);

또 다른 방법은 Java Pluggable Annotation Processing API 를 사용 하여 컴파일시 주석이 달린 모든 클래스를 수집하고 런타임에 사용할 색인 파일을 작성하는 주석 프로세서를 작성하는 것입니다. 이 메커니즘은 ClassIndex 라이브러리 에서 구현됩니다 .

// package-info.java
@IndexSubclasses
package my.package;

// your code
Iterable<Class> classes = ClassIndex.getPackageClasses("my.package");

Java 컴파일러가 클래스 경로에서 발견 된 프로세서를 자동으로 감지하므로 스캔이 완전히 자동화되므로 추가 설정이 필요하지 않습니다.


이것은 Jar에 패키지 된 클래스를 발견합니까? 그것은 나를 위해 작동하지 않는 것 같습니다.
ass

어떤 도구를 사용하려고합니까?
Sławek

Reflections lib를 사용하고 있습니다. 그러나 나는이 라이브러리의 최신 버전에 대해 @Aleksander Blomskøld가 언급 한 해결 방법을 따른 후에 효과를 얻었습니다.
ass

안녕하세요, 나는 일식을 사용하고 그것을 작동시킬 수 없습니다, ClassIndex.getPackageClasses ( "my.package")는 빈지도를 반환
Juan

11

주어진 패키지에 모든 클래스를 나열하는 가장 강력한 메커니즘은 현재 ClassGraph 입니다. 새로운 JPMS 모듈 시스템을 포함하여 가장 광범위한 클래스 경로 지정 메커니즘을 처리하기 때문 입니다. (저는 저자입니다.)

List<String> classNames = new ArrayList<>();
try (ScanResult scanResult = new ClassGraph().whitelistPackages("my.package")
        .enableClassInfo().scan()) {
    classNames.addAll(scanResult.getAllClasses().getNames());
}

5

내가하는 방법은 다음과 같습니다. 모든 하위 폴더 (하위 패키지)를 스캔하고 익명 클래스를로드하려고 시도하지 않습니다.

   /**
   * Attempts to list all the classes in the specified package as determined
   * by the context class loader, recursively, avoiding anonymous classes
   * 
   * @param pckgname
   *            the package name to search
   * @return a list of classes that exist within that package
   * @throws ClassNotFoundException
   *             if something went wrong
   */
  private static List<Class> getClassesForPackage(String pckgname) throws ClassNotFoundException {
      // This will hold a list of directories matching the pckgname. There may be more than one if a package is split over multiple jars/paths
      ArrayList<File> directories = new ArrayList<File>();
      String packageToPath = pckgname.replace('.', '/');
      try {
          ClassLoader cld = Thread.currentThread().getContextClassLoader();
          if (cld == null) {
              throw new ClassNotFoundException("Can't get class loader.");
          }

          // Ask for all resources for the packageToPath
          Enumeration<URL> resources = cld.getResources(packageToPath);
          while (resources.hasMoreElements()) {
              directories.add(new File(URLDecoder.decode(resources.nextElement().getPath(), "UTF-8")));
          }
      } catch (NullPointerException x) {
          throw new ClassNotFoundException(pckgname + " does not appear to be a valid package (Null pointer exception)");
      } catch (UnsupportedEncodingException encex) {
          throw new ClassNotFoundException(pckgname + " does not appear to be a valid package (Unsupported encoding)");
      } catch (IOException ioex) {
          throw new ClassNotFoundException("IOException was thrown when trying to get all resources for " + pckgname);
      }

      ArrayList<Class> classes = new ArrayList<Class>();
      // For every directoryFile identified capture all the .class files
      while (!directories.isEmpty()){
          File directoryFile  = directories.remove(0);             
          if (directoryFile.exists()) {
              // Get the list of the files contained in the package
              File[] files = directoryFile.listFiles();

              for (File file : files) {
                  // we are only interested in .class files
                  if ((file.getName().endsWith(".class")) && (!file.getName().contains("$"))) {
                      // removes the .class extension
                      int index = directoryFile.getPath().indexOf(packageToPath);
                      String packagePrefix = directoryFile.getPath().substring(index).replace('/', '.');;                          
                    try {                  
                      String className = packagePrefix + '.' + file.getName().substring(0, file.getName().length() - 6);                            
                      classes.add(Class.forName(className));                                
                    } catch (NoClassDefFoundError e)
                    {
                      // do nothing. this class hasn't been found by the loader, and we don't care.
                    }
                  } else if (file.isDirectory()){ // If we got to a subdirectory
                      directories.add(new File(file.getPath()));                          
                  }
              }
          } else {
              throw new ClassNotFoundException(pckgname + " (" + directoryFile.getPath() + ") does not appear to be a valid package");
          }
      }
      return classes;
  }  

4

이 문제를 해결하는 간단한 github 프로젝트를 만들었습니다.

https://github.com/ddopson/java-class-enumerator

파일 기반 클래스 경로와 jar 파일 모두에서 작동합니다.

프로젝트를 체크 아웃 한 후 'make'를 실행하면 다음과 같이 인쇄됩니다.

 Cleaning...
rm -rf build/
 Building...
javac -d build/classes src/pro/ddopson/ClassEnumerator.java src/test/ClassIShouldFindOne.java src/test/ClassIShouldFindTwo.java src/test/subpkg/ClassIShouldFindThree.java src/test/TestClassEnumeration.java
 Making JAR Files...
jar cf build/ClassEnumerator_test.jar -C build/classes/ . 
jar cf build/ClassEnumerator.jar -C build/classes/ pro
 Running Filesystem Classpath Test...
java -classpath build/classes test.TestClassEnumeration
ClassDiscovery: Package: 'test' becomes Resource: 'file:/Users/Dopson/work/other/java-class-enumeration/build/classes/test'
ClassDiscovery: Reading Directory '/Users/Dopson/work/other/java-class-enumeration/build/classes/test'
ClassDiscovery: FileName 'ClassIShouldFindOne.class'  =>  class 'test.ClassIShouldFindOne'
ClassDiscovery: FileName 'ClassIShouldFindTwo.class'  =>  class 'test.ClassIShouldFindTwo'
ClassDiscovery: FileName 'subpkg'  =>  class 'null'
ClassDiscovery: Reading Directory '/Users/Dopson/work/other/java-class-enumeration/build/classes/test/subpkg'
ClassDiscovery: FileName 'ClassIShouldFindThree.class'  =>  class 'test.subpkg.ClassIShouldFindThree'
ClassDiscovery: FileName 'TestClassEnumeration.class'  =>  class 'test.TestClassEnumeration'
 Running JAR Classpath Test...
java -classpath build/ClassEnumerator_test.jar  test.TestClassEnumeration
ClassDiscovery: Package: 'test' becomes Resource: 'jar:file:/Users/Dopson/work/other/java-class-enumeration/build/ClassEnumerator_test.jar!/test'
ClassDiscovery: Reading JAR file: '/Users/Dopson/work/other/java-class-enumeration/build/ClassEnumerator_test.jar'
ClassDiscovery: JarEntry 'META-INF/'  =>  class 'null'
ClassDiscovery: JarEntry 'META-INF/MANIFEST.MF'  =>  class 'null'
ClassDiscovery: JarEntry 'pro/'  =>  class 'null'
ClassDiscovery: JarEntry 'pro/ddopson/'  =>  class 'null'
ClassDiscovery: JarEntry 'pro/ddopson/ClassEnumerator.class'  =>  class 'null'
ClassDiscovery: JarEntry 'test/'  =>  class 'null'
ClassDiscovery: JarEntry 'test/ClassIShouldFindOne.class'  =>  class 'test.ClassIShouldFindOne'
ClassDiscovery: JarEntry 'test/ClassIShouldFindTwo.class'  =>  class 'test.ClassIShouldFindTwo'
ClassDiscovery: JarEntry 'test/subpkg/'  =>  class 'null'
ClassDiscovery: JarEntry 'test/subpkg/ClassIShouldFindThree.class'  =>  class 'test.subpkg.ClassIShouldFindThree'
ClassDiscovery: JarEntry 'test/TestClassEnumeration.class'  =>  class 'test.TestClassEnumeration'
 Tests Passed. 

내 다른 답변 도 참조하십시오


4

예, API를 거의 사용하지 않는 것이 좋습니다. 여기서 내가 좋아하는 방법은 최대 절전 모드를 사용하고 특정 주석으로 주석이 달린 클래스를 찾아야하는이 문제에 직면했습니다.

이 클래스를 사용하여 선택하려는 클래스를 표시하십시오.

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface EntityToBeScanned {

}

그런 다음 수업에 다음과 같이 표시하십시오.

@EntityToBeScanned 
public MyClass{

}

다음과 같은 메소드가있는이 유틸리티 클래스를 만드십시오.

public class ClassScanner {

    public static Set<Class<?>> allFoundClassesAnnotatedWithEntityToBeScanned(){
        Reflections reflections = new Reflections(".*");
        Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(EntityToBeScanned.class);
        return annotated;
    }

}

발견 된 클래스 세트 를 얻으려면 allFoundClassesAnnotatedWithEntityToBeScanned () 메소드를 호출하십시오 .

아래에 주어진 라이브러리가 필요합니다.

<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>21.0</version>
    </dependency>
<!-- https://mvnrepository.com/artifact/org.javassist/javassist -->
<dependency>
    <groupId>org.javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.22.0-CR1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.reflections/reflections -->
<dependency>
    <groupId>org.reflections</groupId>
    <artifactId>reflections</artifactId>
    <version>0.9.10</version>
</dependency>

3

클래스 경로에서 모든 클래스 로더 항목을 찾아야합니다.

    String pkg = "org/apache/commons/lang";
    ClassLoader cl = ClassLoader.getSystemClassLoader();
    URL[] urls = ((URLClassLoader) cl).getURLs();
    for (URL url : urls) {
        System.out.println(url.getFile());
        File jar = new File(url.getFile());
        // ....
    }   

entry가 디렉토리이면 오른쪽 하위 디렉토리를 찾으십시오.

if (jar.isDirectory()) {
    File subdir = new File(jar, pkg);
    if (!subdir.exists())
        continue;
    File[] files = subdir.listFiles();
    for (File file : files) {
        if (!file.isFile())
            continue;
        if (file.getName().endsWith(".class"))
            System.out.println("Found class: "
                    + file.getName().substring(0,
                            file.getName().length() - 6));
    }
}   

항목이 파일이고 jar 인 경우 ZIP 항목을 검사하십시오.

else {
    // try to open as ZIP
    try {
        ZipFile zip = new ZipFile(jar);
        for (Enumeration<? extends ZipEntry> entries = zip
                .entries(); entries.hasMoreElements();) {
            ZipEntry entry = entries.nextElement();
            String name = entry.getName();
            if (!name.startsWith(pkg))
                continue;
            name = name.substring(pkg.length() + 1);
            if (name.indexOf('/') < 0 && name.endsWith(".class"))
                System.out.println("Found class: "
                        + name.substring(0, name.length() - 6));
        }
    } catch (ZipException e) {
        System.out.println("Not a ZIP: " + e.getMessage());
    } catch (IOException e) {
        System.err.println(e.getMessage());
    }
}

이제 패키지와 함께 모든 클래스 이름을 가지면 리플렉션으로로드하고 클래스 또는 인터페이스인지 분석 할 수 있습니다.


Jar 파일에서 패키지에 무엇을 입력 하시겠습니까?
Kyle Bridenstine

이 예제는 하위 패키지를 거치지 않습니다. 아마 그것은 관심이 있을지도 모릅니다 ... @ mr-tea 원하는 패키지를 지정하십시오. 나는 이것을 프로젝트에 넣고, 그 프로젝트 내에 테스트 패키지를 지정하고 그것을 컴파일하고 패키지화하고 JAR의 주요 메소드에서 예제를 호출했다. 매력처럼 일했다. :)
Christian

3

리플렉션 라이브러리를 사용하려고 시도했지만 사용하는 데 문제가 있었으며 패키지의 클래스를 얻기 위해 포함해야 할 항아리가 너무 많았습니다.

이 중복 질문에서 찾은 해결책을 게시 할 것입니다 : 패키지에서 모든 클래스 이름을 얻는 방법?

대답은 sp00m에 의해 작성되었습니다 ; 작동하도록 몇 가지 수정 사항을 추가했습니다.

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;

public final class ClassFinder {

    private final static char DOT = '.';
    private final static char SLASH = '/';
    private final static String CLASS_SUFFIX = ".class";
    private final static String BAD_PACKAGE_ERROR = "Unable to get resources from path '%s'. Are you sure the given '%s' package exists?";

    public final static List<Class<?>> find(final String scannedPackage) {
        final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        final String scannedPath = scannedPackage.replace(DOT, SLASH);
        final Enumeration<URL> resources;
        try {
            resources = classLoader.getResources(scannedPath);
        } catch (IOException e) {
            throw new IllegalArgumentException(String.format(BAD_PACKAGE_ERROR, scannedPath, scannedPackage), e);
        }
        final List<Class<?>> classes = new LinkedList<Class<?>>();
        while (resources.hasMoreElements()) {
            final File file = new File(resources.nextElement().getFile());
            classes.addAll(find(file, scannedPackage));
        }
        return classes;
    }

    private final static List<Class<?>> find(final File file, final String scannedPackage) {
        final List<Class<?>> classes = new LinkedList<Class<?>>();
        if (file.isDirectory()) {
            for (File nestedFile : file.listFiles()) {
                classes.addAll(find(nestedFile, scannedPackage));
            }
        //File names with the $1, $2 holds the anonymous inner classes, we are not interested on them. 
        } else if (file.getName().endsWith(CLASS_SUFFIX) && !file.getName().contains("$")) {

            final int beginIndex = 0;
            final int endIndex = file.getName().length() - CLASS_SUFFIX.length();
            final String className = file.getName().substring(beginIndex, endIndex);
            try {
                final String resource = scannedPackage + DOT + className;
                classes.add(Class.forName(resource));
            } catch (ClassNotFoundException ignore) {
            }
        }
        return classes;
    }

}

그것을 사용하려면이 예제에서 언급 한 sp00n과 같이 find 메소드를 호출하십시오. 필요한 경우 클래스 인스턴스 작성을 추가했습니다.

List<Class<?>> classes = ClassFinder.find("com.package");

ExcelReporting excelReporting;
for (Class<?> aClass : classes) {
    Constructor constructor = aClass.getConstructor();
    //Create an object of the class type
    constructor.newInstance();
    //...
}

3

방금 util 클래스를 작성했습니다. 테스트 메소드가 포함되어 있습니다. 확인 할 수 있습니다 ~

IteratePackageUtil.java :

package eric.j2se.reflect;

import java.util.Set;

import org.reflections.Reflections;
import org.reflections.scanners.ResourcesScanner;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import org.reflections.util.FilterBuilder;

/**
 * an util to iterate class in a package,
 * 
 * @author eric
 * @date Dec 10, 2013 12:36:46 AM
 */
public class IteratePackageUtil {
    /**
     * <p>
     * Get set of all class in a specified package recursively. this only support lib
     * </p>
     * <p>
     * class of sub package will be included, inner class will be included,
     * </p>
     * <p>
     * could load class that use the same classloader of current class, can't load system packages,
     * </p>
     * 
     * @param pkg
     *            path of a package
     * @return
     */
    public static Set<Class<? extends Object>> getClazzSet(String pkg) {
        // prepare reflection, include direct subclass of Object.class
        Reflections reflections = new Reflections(new ConfigurationBuilder().setScanners(new SubTypesScanner(false), new ResourcesScanner())
                .setUrls(ClasspathHelper.forClassLoader(ClasspathHelper.classLoaders(new ClassLoader[0])))
                .filterInputsBy(new FilterBuilder().includePackage(pkg)));

        return reflections.getSubTypesOf(Object.class);
    }

    public static void test() {
        String pkg = "org.apache.tomcat.util";

        Set<Class<? extends Object>> clazzSet = getClazzSet(pkg);
        for (Class<? extends Object> clazz : clazzSet) {
            System.out.println(clazz.getName());
        }
    }

    public static void main(String[] args) {
        test();
    }
}

3

@RunWith(Parameterized.class)Maven을 사용할 때 Aleksander Blomskøld의 솔루션 이 매개 변수 테스트에 적합하지 않았습니다 . 테스트 이름이 올 바르고 발견되었지만 실행되지 않은 위치는 다음과 같습니다.

-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running some.properly.named.test.run.with.maven.SomeTest
Tests run: 0, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.123 sec

비슷한 문제가 여기 에보고 되었습니다 .

필자의 경우 @Parameters패키지에 각 클래스의 인스턴스를 만듭니다. 테스트는 IDE에서 로컬로 실행될 때 잘 작동했습니다. 그러나 Maven을 실행할 때 Aleksander Blomskøld의 솔루션으로 찾은 클래스는 없습니다.

Aleksander Blomskøld의 답변에 대한 David Pärsson의 의견에서 영감을 얻은 다음과 같은 내용을 사용하여 작업했습니다.

Reflections reflections = new Reflections(new ConfigurationBuilder()
            .setScanners(new SubTypesScanner(false /* don't exclude Object.class */), new ResourcesScanner())
            .addUrls(ClasspathHelper.forJavaClassPath()) 
            .filterInputsBy(new FilterBuilder()
            .include(FilterBuilder.prefix(basePackage))));

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

3

이건 어때?

public static List<Class<?>> getClassesForPackage(final String pkgName) throws IOException, URISyntaxException {
    final String pkgPath = pkgName.replace('.', '/');
    final URI pkg = Objects.requireNonNull(ClassLoader.getSystemClassLoader().getResource(pkgPath)).toURI();
    final ArrayList<Class<?>> allClasses = new ArrayList<Class<?>>();

    Path root;
    if (pkg.toString().startsWith("jar:")) {
        try {
            root = FileSystems.getFileSystem(pkg).getPath(pkgPath);
        } catch (final FileSystemNotFoundException e) {
            root = FileSystems.newFileSystem(pkg, Collections.emptyMap()).getPath(pkgPath);
        }
    } else {
        root = Paths.get(pkg);
    }

    final String extension = ".class";
    try (final Stream<Path> allPaths = Files.walk(root)) {
        allPaths.filter(Files::isRegularFile).forEach(file -> {
            try {
                final String path = file.toString().replace('/', '.');
                final String name = path.substring(path.indexOf(pkgName), path.length() - extension.length());
                allClasses.add(Class.forName(name));
            } catch (final ClassNotFoundException | StringIndexOutOfBoundsException ignored) {
            }
        });
    }
    return allClasses;
}

그런 다음 함수를 오버로드 할 수 있습니다.

public static List<Class<?>> getClassesForPackage(final Package pkg) throws IOException, URISyntaxException {
    return getClassesForPackage(pkg.getName());
}

테스트해야 할 경우 :

public static void main(final String[] argv) throws IOException, URISyntaxException {
    for (final Class<?> cls : getClassesForPackage("my.package")) {
        System.out.println(cls);
    }
    for (final Class<?> cls : getClassesForPackage(MyClass.class.getPackage())) {
        System.out.println(cls);
    }
}

IDE에 가져 오기 도우미가없는 경우 :

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.FileSystemNotFoundException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;

효과가있다:

  • IDE에서

  • JAR 파일

  • 외부 의존성이없는


2

거의 모든 대답 Reflections은 파일 시스템에서 클래스 파일을 사용 하거나 읽습니다. 파일 시스템에서 클래스를 읽으려고하면 응용 프로그램을 JAR 또는 기타로 패키지화 할 때 오류가 발생할 수 있습니다. 또한 해당 목적을 위해 별도의 라이브러리를 사용하지 않을 수도 있습니다.

여기에 순수한 자바이고 파일 시스템에 의존하지 않는 또 다른 접근법이 있습니다.

import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

public class PackageUtil {

    public static Collection<Class> getClasses(final String pack) throws Exception {
        final StandardJavaFileManager fileManager = ToolProvider.getSystemJavaCompiler().getStandardFileManager(null, null, null);
        return StreamSupport.stream(fileManager.list(StandardLocation.CLASS_PATH, pack, Collections.singleton(JavaFileObject.Kind.CLASS), false).spliterator(), false)
                .map(javaFileObject -> {
                    try {
                        final String[] split = javaFileObject.getName()
                                .replace(".class", "")
                                .replace(")", "")
                                .split(Pattern.quote(File.separator));

                        final String fullClassName = pack + "." + split[split.length - 1];
                        return Class.forName(fullClassName);
                    } catch (ClassNotFoundException e) {
                        throw new RuntimeException(e);
                    }

                })
                .collect(Collectors.toCollection(ArrayList::new));
    }
}

Java 8은 필수는 아닙니다 . 스트림 대신 for 루프를 사용할 수 있습니다. 그리고 당신은 이것을 이렇게 테스트 할 수 있습니다

public static void main(String[] args) throws Exception {
    final String pack = "java.nio.file"; // Or any other package
    PackageUtil.getClasses(pack).stream().forEach(System.out::println);
}

1
이 때문에 매우 유용하지 않습니다 : 필요가있다 JDK를 가지고 사용 ToolProvider.getSystemJavaCompiler()이 코드는하지 스캔 중첩 된 패키지를 않습니다.
v.ladynev

외부 항아리 패키지로 작업 할 수 없습니다.
Enrico Giurin

1

동적 클래스 로더를 사용하지 않는 경우 클래스 경로를 검색하고 각 항목마다 디렉토리 또는 JAR 파일을 검색 할 수 있습니다.


1

언급 할 가치가있는

일부 패키지 아래의 모든 클래스 목록을 원하는 경우 Reflection다음 방법을 사용할 수 있습니다 .

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

Reflections reflections = new Reflections("com.package");
for (String s : reflections.getStore().get(SubTypesScanner.class).values()) {
    myTypes.add(Class.forName(s));
}

그러면 나중에 원하는대로 사용할 수있는 클래스 목록이 생성됩니다.


1

가능하지만 추가 라이브러리 가 없으면 어렵 Reflections습니다 ...
클래스 이름을 얻는 데 필요한 도구가 없기 때문에 어렵습니다.
그리고 나는 ClassFinder수업 코드를 취합니다 .

package play.util;

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

/**
 * Created by LINKOR on 26.05.2017 in 15:12.
 * Date: 2017.05.26
 */
public class FileClassFinder {
private JarFile file;
private boolean trouble;
public FileClassFinder(String filePath) {
    try {
        file = new JarFile(filePath);
    } catch (IOException e) {
        trouble = true;
    }
}

public List<String> findClasses(String pkg) {
    ArrayList<String> classes = new ArrayList<>();
    Enumeration<JarEntry> entries = file.entries();
    while (entries.hasMoreElements()) {
        JarEntry cls = entries.nextElement();
        if (!cls.isDirectory()) {
            String fileName = cls.getName();
            String className = fileName.replaceAll("/",         ".").replaceAll(File.pathSeparator, ".").substring(0, fileName.lastIndexOf('.'));
            if (className.startsWith(pkg)) classes.add(className.substring(pkg.length() + 1));
        }
    }
    return classes;
}
}

0

@Staale의 답변 과 타사 라이브러리에 의존하지 않으려는 시도를 바탕으로 다음 과 같이 첫 번째 패키지 실제 위치를 검사하여 파일 시스템 접근 방식을 구현합니다.

import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
...
Class<?>[] foundClasses = new Class<?>[0];
final ArrayList<Class<?>> foundClassesDyn = new ArrayList<Class<?>>();

new java.io.File(
    klass.getResource(
        "/" + curPackage.replace( "." , "/")
    ).getFile()
).listFiles(
    new java.io.FileFilter() {
        public boolean accept(java.io.File file) {
            final String classExtension = ".class";

            if ( file.isFile()
                && file.getName().endsWith(classExtension)
                // avoid inner classes
                && ! file.getName().contains("$") )
            {
                try {
                    String className = file.getName();
                    className = className.substring(0, className.length() - classExtension.length());
                    foundClassesDyn.add( Class.forName( curPackage + "." + className ) );
                } catch (ClassNotFoundException e) {
                    e.printStackTrace(System.out);
                }
            }

            return false;
        }
    }
);

foundClasses = foundClassesDyn.toArray(foundClasses);

0

관련 클래스 그룹을로드하려는 경우 Spring이 도움이 될 수 있습니다.

Spring은 주어진 인터페이스를 구현하는 모든 클래스의 목록 또는 맵을 한 줄의 코드로 인스턴스화 할 수 있습니다. 목록 또는 맵에는 해당 인터페이스를 구현하는 모든 클래스의 인스턴스가 포함됩니다.

즉, 파일 시스템에서 클래스 목록을로드하는 대신 패키지와 상관없이로드하려는 모든 클래스에서 동일한 인터페이스를 구현하고 스프링을 사용하여 모든 인스턴스를 제공합니다. 이렇게하면 패키지에 관계없이 원하는 모든 클래스를로드 (및 인스턴스화) 할 수 있습니다.

반면에 패키지에 모두 포함시키는 것이 원하는 경우 해당 패키지의 모든 클래스가 지정된 인터페이스를 구현하도록합니다.


0

일반 자바 : FindAllClassesUsingPlainJavaReflectionTest.java

@Slf4j
class FindAllClassesUsingPlainJavaReflectionTest {

  private static final Function<Throwable, RuntimeException> asRuntimeException = throwable -> {
    log.error(throwable.getLocalizedMessage());
    return new RuntimeException(throwable);
  };

  private static final Function<String, Collection<Class<?>>> findAllPackageClasses = basePackageName -> {

    Locale locale = Locale.getDefault();
    Charset charset = StandardCharsets.UTF_8;
    val fileManager = ToolProvider.getSystemJavaCompiler()
                                  .getStandardFileManager(/* diagnosticListener */ null, locale, charset);

    StandardLocation location = StandardLocation.CLASS_PATH;
    JavaFileObject.Kind kind = JavaFileObject.Kind.CLASS;
    Set<JavaFileObject.Kind> kinds = Collections.singleton(kind);
    val javaFileObjects = Try.of(() -> fileManager.list(location, basePackageName, kinds, /* recurse */ true))
                             .getOrElseThrow(asRuntimeException);

    String pathToPackageAndClass = basePackageName.replace(".", File.separator);
    Function<String, String> mapToClassName = s -> {
      String prefix = Arrays.stream(s.split(pathToPackageAndClass))
                            .findFirst()
                            .orElse("");
      return s.replaceFirst(prefix, "")
              .replaceAll(File.separator, ".");
    };

    return StreamSupport.stream(javaFileObjects.spliterator(), /* parallel */ true)
                        .filter(javaFileObject -> javaFileObject.getKind().equals(kind))
                        .map(FileObject::getName)
                        .map(fileObjectName -> fileObjectName.replace(".class", ""))
                        .map(mapToClassName)
                        .map(className -> Try.of(() -> Class.forName(className))
                                             .getOrElseThrow(asRuntimeException))
                        .collect(Collectors.toList());
  };

  @Test
  @DisplayName("should get classes recursively in given package")
  void test() {
    Collection<Class<?>> classes = findAllPackageClasses.apply(getClass().getPackage().getName());
    assertThat(classes).hasSizeGreaterThan(4);
    classes.stream().map(String::valueOf).forEach(log::info);
  }
}

추신 : 오류 처리 등을 위해 상용구를 단순화하기 위해 여기 vavrlombok라이브러리를 사용하고 있습니다.

다른 구현은 내 GitHub daggerok / java-reflection-find-annotated-classes-or-methods repo에서 찾을 수 있습니다.


0

나는 너무 간단한 무언가를 위해 간절히 일하는 것을 찾을 수 없었다. 그래서 여기에, 잠시 동안 조여서 나 스스로 만들었습니다.

    Reflections reflections =
        new Reflections(new ConfigurationBuilder()
                .filterInputsBy(new FilterBuilder().includePackage(packagePath))
                .setUrls(ClasspathHelper.forPackage(packagePath))
                .setScanners(new SubTypesScanner(false)));

    Set<String> typeList = reflections.getAllTypes(); 

0

Spring-land에 있다면 PathMatchingResourcePatternResolver;

  PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
  Resource[] resources = resolver.getResources("classpath*:some/package/name/*.class");

    Arrays.asList(resources).forEach(r->{
        ...
    });

-4

패키지의 모든 클래스가로드되지 않을 수는 있지만 클래스의 패키지를 항상 알고 있기 때문에 불가능합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.