2009-09-30 26 views
12

Có cách nào để lặp qua tất cả các lớp trong classpath không?Java: vòng lặp trên tất cả các lớp trong đường dẫn lớp

Tôi muốn thực hiện một số kiểm tra phản chiếu trên một số lớp thực hiện một giao diện nhất định, nhưng tôi muốn thực hiện nó một cách tự động mà không cần nhập vào lớp nào để kiểm tra.

+1

nếu bạn cung cấp thêm một chút thông tin về lý do bạn muốn thực hiện - như trường hợp bạn đang gặp phải, có thể thực hiện theo cách khác và chưa đạt được kết quả tương tự. – Chii

Trả lời

13

Thư viện Reflections giúp giải quyết vấn đề này. Như những người khác đã nói, nó không phải là hoàn toàn có thể trong tất cả các tình huống tải lớp, nhưng nếu tất cả bạn có là lọ và các tập tin, điều này sẽ làm điều đó đáng tin cậy.

+0

Thực sự tốt đẹp tìm thấy ... Tôi đã tìm kiếm cùng một điều một khi trở lại ... muốn tôi đã chỉ cần hỏi ở đây :) –

+0

Great mate, cảm ơn rất nhiều. – glmxndr

+0

[FastClasspathScanner] (https://github.com/lukehutch/fast-classpath-scanner) cũng hoạt động tốt cho điều này. –

8

Bạn không thể làm điều này một cách trang nhã.

Về cơ bản, trình nạp lớp có thể được yêu cầu tải một tên lớp cụ thể nhưng không thể yêu cầu tất cả các lớp mà nó có thể tải. (Trong trường hợp có thứ gì đó tải các lớp lên web, có thể không khả thi để làm như vậy - bạn không thể tin cậy yêu cầu máy chủ web cho bạn biết tất cả các tệp trong một thư mục cụ thể.)

Nếu classpath của bạn chỉ có giao dịch với hệ thống tệp, bạn có thể tìm thấy tất cả các tệp jar trong các thư mục mở rộng, xem lại các thư mục classpath bình thường và xem bên trong tất cả các tệp jar được chỉ định rõ ràng - nhưng nó sẽ phức tạp và có thể mong manh.

0

bạn không thể không có trình nạp lớp tùy chỉnh.

hãy nghĩ ví dụ về trường hợp bạn tải các lớp của mình từ một máy chủ từ xa thậm chí không cung cấp danh sách thư mục. làm sao bạn có thể lặp lại tất cả các lớp ở đó?

giao diện trình nạp lớp không cung cấp chức năng như vậy. nhưng nếu bạn sử dụng trình nạp lớp của riêng bạn (hoặc mở rộng trình nạp lớp hiện có), bạn có thể thêm hàm này. trong mọi trường hợp, nó không đẹp.

3

Tôi nghĩ rằng bạn sẽ phải kiểm tra nó bằng tay:

String classpath = System.getProperty("java.class.path"); 
String[] locations = classpath.split(System.getProperty("path.separator")); 
// inspect all jar's and class files in these locations, which is a pain in the b%tt 

Hoặc sử dụng một số thư viện của bên thứ ba mà thực hiện điều này (tôi không biết về bất kỳ).

+0

và thậm chí có thể không cung cấp cho bạn mọi lớp có thể tải vào thời điểm đó, vì bạn không biết liệu có bất kỳ trình nạp lớp tùy chỉnh nào không (trừ khi bạn đặc biệt tìm trình nạp lớp và phân tích chúng để xem chúng có tải lớp mới không ... thats như giải quyết các vấn đề ngăn chặn). – Chii

+0

Apache VFS cho phép bạn lặp qua nội dung của các tệp JAR. –

2

Tôi không biết thư viện nào thực hiện việc này, nhưng có các dự án mã nguồn mở thực hiện việc này cho mục đích riêng của họ. Ví dụ: Spring có thể lặp qua các lớp trong đường dẫn lớp để tìm các lớp có chú thích nhất định. Bạn có thể xem cách họ làm điều đó để có được một số ý tưởng. Tôi bắt đầu với các lớp học trong gói org.springframework.context.annotation chẳng hạn như ClassPathScanningCandidateComponentProviderCommonAnnotationBeanPostProcessor.

6

Tôi đã giải quyết vấn đề này cho một trình nạp lớp đơn. Tôi cần sự phản chiếu để viết mã để kiểm tra các bài kiểm tra JUnit và báo cáo về các bài kiểm tra bị bỏ qua.

 /** 
     * 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 
     */ 
     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>(); 
      try { 
       ClassLoader cld = Thread.currentThread().getContextClassLoader(); 
       if (cld == null) { 
        throw new ClassNotFoundException("Can't get class loader."); 
       } 
       String path = pckgname.replace('.', '/'); 
       // Ask for all resources for the path 
       Enumeration<URL> resources = cld.getResources(path); 
       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 directory identified capture all the .class files 
      for (File directory : directories) { 
       if (directory.exists()) { 
        // Get the list of the files contained in the package 
        String[] files = directory.list(); 
        for (String file : files) { 
         // we are only interested in .class files 
         if (file.endsWith(".class")) { 
          // removes the .class extension 
         try 
         { 
          classes.add(Class.forName(pckgname + '.' + file.substring(0, file.length() - 6)));      
         } 
         catch (NoClassDefFoundError e) 
         { 
          // do nothing. this class hasn't been found by the loader, and we don't care. 
         } 
         } 
        } 
       } else { 
        throw new ClassNotFoundException(pckgname + " (" + directory.getPath() + ") does not appear to be a valid package"); 
       } 
      } 
      return classes; 
     } 

tôi dựa trên giải pháp của tôi trên code in this thread:

1

Nếu bạn sử dụng lò xo bối cảnh trong dự án của bạn: Dưới đây là một ví dụ làm việc để liệt kê tất cả các loại với chú thích mong muốn:

/** 
* Lists all types in the given package (recursive) which are annotated by the given annotation. 
* <p> 
* All types which match the criteria are returned, no further checks (interface, abstract, embedded, etc. 
* are performed. 
* <p> 
* 
* @param aAnnotation 
*  the desired annotation type 
* @param aRoot 
*  the package name where to start the search. 
* @return a list of found types 
*/ 
private Collection<? extends Class<?>> findCandidatesByAnnotation(Class<? extends Annotation> aAnnotation, 
                    String aRoot) 
{ 
    List<Class<?>> result = new ArrayList<Class<?>>(); 

    ClassPathScanningCandidateComponentProvider scanner = 
      new ClassPathScanningCandidateComponentProvider(false) 
    { 
     @Override 
     protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) 
     { 
      return true; 
     } 
    }; 

    scanner.addIncludeFilter(new AnnotationTypeFilter(aAnnotation)); 
    Set<BeanDefinition> canditates = scanner.findCandidateComponents(aRoot); 
    for (BeanDefinition beanDefinition : canditates) 
    { 
     try 
     { 
      String classname = beanDefinition.getBeanClassName(); 
      Class<?> clazz = Class.forName(classname); 
      result.add(clazz); 
     } 
     catch (ClassNotFoundException t) 
     { 
      myLog.error("Springs type scanner returns a class name whose class cannot be evaluated!!!", t); 
     } 
    } 

    return result; 
} 

này mã đã được thử nghiệm với spring-context-4.1.1.RELEASE dưới java 8.

Các vấn đề liên quan