1. 개요

대부분의 Java 라이브러리는 JAR 파일 로 사용할 수 있습니다 . 이 사용방법(예제)에서는 명령줄과 Java 프로그램에서 지정된 JAR 파일 내부의 클래스 이름을 가져오는 방법을 설명합니다.

그런 다음 런타임에 지정된 JAR 파일에서 클래스를 로드하는 Java 프로그램 예제를 살펴보겠습니다.

2. JAR 파일의 예

이 사용방법(예제)에서는 JAR 파일에서 클래스 이름을 가져오는 방법을 설명하는 예제로 stripe-0.0.1-SNAPSHOT.jar 파일을 사용합니다.

3.  jar 명령 사용

JDK는 jar 명령 과 함께 제공됩니다  . 이 명령을 tf 옵션 함께 사용 하여 JAR 파일의 내용을 나열할 수 있습니다 .

$ jar tf stripe-0.0.1-SNAPSHOT.jar 
META-INF/
META-INF/MANIFEST.MF
...
templates/result.html
templates/checkout.html
application.properties
com/baeldung/stripe/StripeApplication.class
com/baeldung/stripe/ChargeRequest.class
com/baeldung/stripe/StripeService.class
com/baeldung/stripe/ChargeRequest$Currency.class
...

아카이브 *.class 파일 에만 관심이 있으므로 grep 명령을 사용하여 출력을 필터링할 수 있습니다 .

$ jar tf stripe-0.0.1-SNAPSHOT.jar | grep '\.class$'
com/baeldung/stripe/StripeApplication.class
com/baeldung/stripe/ChargeRequest.class
com/baeldung/stripe/StripeService.class
com/baeldung/stripe/ChargeRequest$Currency.class
com/baeldung/stripe/ChargeController.class
com/baeldung/stripe/CheckoutController.class

이것은 JAR 파일 내부의 클래스 파일 List을 제공합니다.

4. Java에서 JAR 파일의 클래스 이름 가져오기

jar 명령을 사용하여  JAR 파일에서 클래스 이름을 인쇄하는 것은 매우 간단합니다. 그러나 때로는 Java 프로그램의 JAR 파일에서 일부 클래스를 로드하고 싶을 때가 있습니다. 이 경우 명령줄 출력이 충분하지 않습니다.

목표를 달성하려면 Java 프로그램에서 JAR 파일스캔 하고 클래스 이름을 가져와야 합니다.

JarFile  및 JarEntry 클래스를 사용하여 예제 JAR 파일에서 클래스 이름을 추출하는 방법을 살펴보겠습니다 .

public static Set<String> getClassNamesFromJarFile(File givenFile) throws IOException {
    Set<String> classNames = new HashSet<>();
    try (JarFile jarFile = new JarFile(givenFile)) {
        Enumeration<JarEntry> e = jarFile.entries();
        while (e.hasMoreElements()) {
            JarEntry jarEntry = e.nextElement();
            if (jarEntry.getName().endsWith(".class")) {
                String className = jarEntry.getName()
                  .replace("/", ".")
                  .replace(".class", "");
                classNames.add(className);
            }
        }
        return classNames;
    }
}

이제 위 메서드의 코드를 자세히 살펴보고 어떻게 작동하는지 이해해 보겠습니다.

  • try (JarFile jarFile = new JarFile(givenFile)) – 여기에서는 주어진 File 객체 에서 jarFile 을 가져오기 위해 try-with-resources 문사용했습니다.
  • if (jarEntry.getName().endsWith(".class")){…} – 각 클래스 jarEntry를 가져 오고 클래스 파일의 경로를 정규화된 클래스 이름으로 변경합니다(예: "package1/package2/SomeType 변경). class""package1.package2.SomeType"으로

메서드가 단위 테스트 메서드를 통해 예제 JAR 파일에서 클래스 이름을 추출할 수 있는지 확인합시다.

private static final String JAR_PATH = "example-jar/stripe-0.0.1-SNAPSHOT.jar";
private static final Set<String> EXPECTED_CLASS_NAMES = Sets.newHashSet(
  "com.baeldung.stripe.StripeApplication",
  "com.baeldung.stripe.ChargeRequest",
  "com.baeldung.stripe.StripeService",
  "com.baeldung.stripe.ChargeRequest$Currency",
  "com.baeldung.stripe.ChargeController",
  "com.baeldung.stripe.CheckoutController");

@Test
public void givenJarFilePath_whenLoadClassNames_thenGetClassNames() throws IOException, URISyntaxException {
    File jarFile = new File(
      Objects.requireNonNull(getClass().getClassLoader().getResource(JAR_PATH)).toURI());

    Set<String> classNames = GetClassNamesFromJar.getClassNamesFromJarFile(jarFile);

    Assert.assertEquals(EXPECTED_CLASS_NAMES, classNames);
}

5. Java의 JAR 파일에서 클래스 가져오기

JAR 파일에서 클래스 이름을 가져오는 방법을 살펴보았습니다. 때로는 런타임에 JAR 파일에서 일부 클래스를 동적으로 로드하고 싶을 때가 있습니다.

이 경우 getClassNamesFromJarFile 메소드를 사용하여 지정된 JAR 파일에서 클래스 이름을 먼저 가져올 수 있습니다 .

다음으로 필요한 클래스를 이름으로 로드 하는 ClassLoader만들 수 있습니다 .

public static Set<Class> getClassesFromJarFile(File jarFile) throws IOException, ClassNotFoundException {
    Set<String> classNames = getClassNamesFromJarFile(jarFile);
    Set<Class> classes = new HashSet<>(classNames.size());
    try (URLClassLoader cl = URLClassLoader.newInstance(
           new URL[] { new URL("jar:file:" + jarFile + "!/") })) {
        for (String name : classNames) {
            Class clazz = cl.loadClass(name); // Load the class by its name
            classes.add(clazz);
        }
    }
    return classes;
}

위의 방법 에서 클래스를 로드하기 위해 URLClassLoader 객체를 만들었습니다 . 구현은 매우 간단합니다.

그러나 JAR URL의 구문을 약간 설명할 가치가 있습니다. 유효한 JAR URL에는 " jar: + [JAR 파일의 위치] + !/"의 세 부분이 포함 됩니다. 

종료 " !/ "는 JAR URL이 전체 JAR 파일을 참조함을 나타냅니다.  몇 가지 JAR URL 예제를 살펴보겠습니다.

jar:http://www.example.com/some_jar_file.jar!/
jar:file:/local/path/to/some_jar_file.jar!/
jar:file:/C:/windows/path/to/some_jar_file.jar!/

우리에 getClassesFromJarFile의 방법, JAR 파일이 로컬 파일 시스템에있는, 따라서 URL의 접두사는 " 파일 : ".

이제 우리의 메서드가 예상되는 모든 Class 객체를 가져올 수 있는지 확인하는 테스트 메서드를 작성해 보겠습니다 .

@Test
public void givenJarFilePath_whenLoadClass_thenGetClassObjects()
  throws IOException, ClassNotFoundException, URISyntaxException {
    File jarFile
      = new File(Objects.requireNonNull(getClass().getClassLoader().getResource(JAR_PATH)).toURI());
    Set<Class> classes = GetClassNamesFromJar.getClassesFromJarFile(jarFile);
    Set<String> names = classes.stream().map(Class::getName).collect(Collectors.toSet());
    Assert.assertEquals(EXPECTED_CLASS_NAMES, names);
}

필요한 Class 객체가 있으면 Java 리플렉션 을 사용하여 클래스의 인스턴스를 만들고 메서드를 호출할 수 있습니다.

6. 결론

이 기사에서는 주어진 JAR 파일에서 클래스 이름을 가져오는 두 가지 다른 접근 방식을 배웠습니다.

항아리 명령은 클래스 이름을 인쇄 할 수 있습니다. JAR 파일에 주어진 클래스가 포함되어 있는지 확인해야 하는 경우 매우 편리합니다. 그러나 실행 중인 Java 프로그램에서 클래스 이름을 가져와야 하는 경우 JarFile 및  JarEntry 가 이를 달성하는 데 도움이 될 수 있습니다.

마지막으로 런타임에 JAR 파일에서 클래스를 로드하는 Java 프로그램의 예도 보았습니다.

항상 그렇듯이 기사의 전체 소스 코드는 GitHub에서 사용할 수 있습니다 .

Junit footer banner