2012-06-13 27 views
49

Tôi đang cố gắng tạo một công cụ Java sẽ quét cấu trúc của một ứng dụng Java và cung cấp một số thông tin có ý nghĩa. Để thực hiện điều này, tôi cần có khả năng quét tất cả các tệp .class từ vị trí dự án (JAR/WAR hoặc chỉ một thư mục) và sử dụng sự phản chiếu để đọc về các phương thức của chúng. Điều này chứng minh là gần như không thể.Làm thế nào để tải các lớp học tại thời gian chạy từ một thư mục hoặc JAR?

Tôi có thể tìm thấy rất nhiều giải pháp dựa trên URLClassloader cho phép tôi tải các lớp cụ thể từ thư mục/kho lưu trữ, nhưng không có lớp nào cho phép tôi tải lớp mà không có bất kỳ thông tin nào về tên lớp hoặc cấu trúc gói.

CHỈNH SỬA: Tôi nghĩ rằng tôi đã nói sai điều này. Vấn đề của tôi không phải là tôi không thể có được tất cả các tập tin lớp học, tôi có thể làm điều đó với đệ quy vv và xác định vị trí chúng đúng cách. Vấn đề của tôi là lấy một đối tượng Class cho mỗi tệp lớp.

+1

Làm cách nào để bạn tải lớp học bỏ qua tên đầy đủ của nó? Tôi không nghĩ rằng điều này là có thể. –

+4

Câu trả lời bên dưới sẽ cho phép bạn định vị tệp lớp nhưng tùy thuộc vào quy mô dự án của bạn, nó có thể đáng giá bằng [asm] (http://asm.ow2.org/) hoặc [javassist] (http: // www.csg.ci.iu-tokyo.ac.jp/~chiba/javassist/). Chúng sẽ cho phép bạn phân tích các lớp mà không cần tải chúng vào bộ nhớ. –

Trả lời

87

Mã sau tải tất cả các lớp từ tệp JAR. Nó không cần phải biết bất cứ điều gì về các lớp học. Tên của các lớp được trích xuất từ ​​JarEntry.

JarFile jarFile = new JarFile(pathToJar); 
Enumeration<JarEntry> e = jarFile.entries(); 

URL[] urls = { new URL("jar:file:" + pathToJar+"!/") }; 
URLClassLoader cl = URLClassLoader.newInstance(urls); 

while (e.hasMoreElements()) { 
    JarEntry je = e.nextElement(); 
    if(je.isDirectory() || !je.getName().endsWith(".class")){ 
     continue; 
    } 
    // -6 because of .class 
    String className = je.getName().substring(0,je.getName().length()-6); 
    className = className.replace('/', '.'); 
    Class c = cl.loadClass(className); 

} 

chỉnh sửa:

Như đã đề cập trong các ý kiến ​​trên, Javassist sẽ cũng là một khả năng. Khởi tạo một ClassPool ở đâu đó trước khi vòng lặp while tạo đoạn mã trên, và thay vì tải lớp với bộ nạp lớp, bạn có thể tạo một đối tượng CtClass:

ClassPool cp = ClassPool.getDefault(); 
... 
CtClass ctClass = cp.get(className); 

Từ ctClass, bạn có thể nhận được tất cả các phương pháp, các lĩnh vực , các lớp lồng nhau, .... Hãy nhìn vào các api Javassist: https://jboss-javassist.github.io/javassist/html/index.html

+1

Xin lỗi để đưa lên một bài cũ (cảm ơn bạn cho câu trả lời này btw, nó làm việc cho tôi). Tuy nhiên, 'URL mới (" jar: file: "+ pathToJar +"!/")' Gây ra một 'ClassNotFoundException'. Những gì đã làm việc cho tôi là: 'URL [] url = {URL mới (" jar: "+ pathToJar +"!/")};' Không chắc tại sao? Hy vọng nó sẽ giúp người khác. – taylorcressy

+0

Đẹp. Bạn có thể muốn đóng jarFile không? thử {...} cuối cùng {jarFile.close(); } –

+0

Liên kết đã chết. –

6

để làm điều này, tôi cần để có thể quét tất cả các file .class từ vị trí dự án (JAR/WAR hoặc chỉ một thư mục)

Quét tất cả các tệp trong một thư mục rất đơn giản. Một tùy chọn là gọi số File.listFiles() trên số File biểu thị thư mục, sau đó lặp lại mảng kết quả. Để di chuyển cây của các thư mục lồng nhau, hãy sử dụng đệ quy.

Quét tệp của tệp JAR có thể được thực hiện bằng API JarFile ... và bạn không cần phải recurse để duyệt qua "thư mục lồng nhau".

Không có điều nào đặc biệt phức tạp. Chỉ cần đọc javadoc và bắt đầu viết mã.

8

Liệt kê Tất cả các lớp bên trong tệp jar.

public static List getClasseNames(String jarName) { 
    ArrayList classes = new ArrayList(); 

    if (debug) 
     System.out.println("Jar " + jarName); 
    try { 
     JarInputStream jarFile = new JarInputStream(new FileInputStream(
       jarName)); 
     JarEntry jarEntry; 

     while (true) { 
      jarEntry = jarFile.getNextJarEntry(); 
      if (jarEntry == null) { 
       break; 
      } 
      if (jarEntry.getName().endsWith(".class")) { 
       if (debug) 
        System.out.println("Found " 
          + jarEntry.getName().replaceAll("/", "\\.")); 
       classes.add(jarEntry.getName().replaceAll("/", "\\.")); 
      } 
     } 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
    return classes; 
} 
Các vấn đề liên quan