2009-11-27 14 views
11

可以說我有一個java包commands其中包含所有繼承自ICommand的類可以以某種方式獲得所有這些類嗎?我正在鎖定以下幾行內容:獲取包中的所有類

Package p = Package.getPackage("commands"); 
Class<ICommand>[] c = p.getAllPackagedClasses(); //not real 

是這樣的可能嗎?

+0

請編輯這個清晰。什麼是'javapackage'。什麼是'命令願望'。 – bmargulies 2009-11-27 21:00:17

+0

主題主題完美:http://google.com/search?q=Getting+all+Classes+from+a+Package =) – BalusC 2009-11-27 21:10:07

+0

@balusc,但/ correct/answer很難。 – 2009-11-28 09:09:17

回答

14

這裏有一個簡單的例子,假設類不是JAR包裝:

// Prepare. 
String packageName = "com.example.commands"; 
List<Class<ICommand>> commands = new ArrayList<Class<ICommand>>(); 
URL root = Thread.currentThread().getContextClassLoader().getResource(packageName.replace(".", "/")); 

// Filter .class files. 
File[] files = new File(root.getFile()).listFiles(new FilenameFilter() { 
    public boolean accept(File dir, String name) { 
     return name.endsWith(".class"); 
    } 
}); 

// Find classes implementing ICommand. 
for (File file : files) { 
    String className = file.getName().replaceAll(".class$", ""); 
    Class<?> cls = Class.forName(packageName + "." + className); 
    if (ICommand.class.isAssignableFrom(cls)) { 
     commands.add((Class<ICommand>) cls); 
    } 
} 
+3

+1,儘管有一點小小的改進:''root.getFile()'應該是'URLDecoder.decode(root.getFile(),「UTF-8」)''在classloader'路徑中有空格的情況下。 'getResource()'會將其轉換爲'%20',以及其他字符,我想。 – Aquillo 2013-05-04 10:12:28

1

從public Classloader.getResources(String name)開始。請向類加載器提供與您感興趣的包中的每個名稱相對應的類。重複所有相關的類加載器。

1

是的,但它不是最容易做的事情。這有很多問題。並非所有的課程都很容易找到。某些類可以在一個:罐,作爲一個類文件,通過網絡等

看看at this thread.

爲了確保他們的ICommand的類型,那麼你將不得不使用反射來檢查繼承類。

3

這是一個使用Spring的實用方法。關於模式

詳細信息,可以發現here

public static List<Class> listMatchingClasses(String matchPattern) throws IOException { 
    List<Class> classes = new LinkedList<Class>(); 
    PathMatchingResourcePatternResolver scanner = new PathMatchingResourcePatternResolver(); 
    Resource[] resources = scanner.getResources(matchPattern); 

    for (Resource resource : resources) { 
     Class<?> clazz = getClassFromResource(resource); 
     classes.add(clazz); 
    } 

    return classes; 
} 



public static Class getClassFromResource(Resource resource) { 
    try { 
     String resourceUri = resource.getURI().toString(); 
     resourceUri = resourceUri.replace(esourceUri.indexOf(".class"), "").replace("/", "."); 
     // try printing the resourceUri before calling forName, to see if it is OK. 
     return Class.forName(resourceUri); 
    } catch (Exception ex) { 
     ex.printStackTrace(); 
    } 
    return null; 
} 
1

這將是我們需要一個非常有用的工具, JDK應該提供一些支持。

但是在構建過程中可能會做得更好。你知道你所有的類文件在哪裏,你可以靜態地檢查它們並建立一個圖表。在運行時你可以查詢這個圖來獲得所有的子類型。這需要更多的工作,但我相信它確實屬於構建過程。

+0

類似於JSR-199 API?請參閱http://stackoverflow.com/questions/1810614/getting-all-classes-from-a-package/1811120#1811120 – 2009-11-28 00:31:30

6

下面,從javax.tools.*使用JSR-199 API即類的實現:

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

JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 
StandardJavaFileManager fileManager = compiler.getStandardFileManager(
     null, null, null); 

Location location = StandardLocation.CLASS_PATH; 
String packageName = "commands"; 
Set<JavaFileObject.Kind> kinds = new HashSet<JavaFileObject.Kind>(); 
kinds.add(JavaFileObject.Kind.CLASS); 
boolean recurse = false; 

Iterable<JavaFileObject> list = fileManager.list(location, packageName, 
     kinds, recurse); 

for (JavaFileObject javaFileObject : list) { 
    commands.add(javaFileObject.getClass()); 
} 
+0

有趣的是,但是在Eclipse和CLI中'ToolProvider.getSystemJavaCompiler()'返回'null'?是更需要的東西? – BalusC 2009-11-28 00:51:19

+1

挖掘之後,這顯然需要一個JDK而不是JRE(但是我在CLI中有這個,稍後我會挖掘爲什麼這種方法不起作用)。這意味着您需要在最終/產品環境中安裝JDK才能使其運行。這可能是個不錯的選擇。 – BalusC 2009-11-28 00:59:49

+2

事實上,你需要一個JDK來獲得一個非'null'編譯器對象(見http://bit.ly/89HtA0),所以這個API並不是真正的目標桌面。儘管(大多數應用程序服務器畢竟使用JDK,例如用於JSP編譯),但在服務器端應該沒問題。但當然,可能有例外。實際上,我只是想表明JDK中有一些支持,即使這不是Compiler API的完美用例(這裏的用法很糟糕)。這個API可以做更多的事情,例如編譯動態地在內存中生成的Java源代碼(這已經更有趣了)。 – 2009-11-28 02:50:51

0

使用Johannes Link's ClasspathSuite,我能做到這一點是這樣的:

import org.junit.extensions.cpsuite.ClassTester; 
import org.junit.extensions.cpsuite.ClasspathClassesFinder; 

public static List<Class<?>> getClasses(final Package pkg, final boolean includeChildPackages) { 
    return new ClasspathClassesFinder(new ClassTester() { 
     @Override public boolean searchInJars() { return true; } 
     @Override public boolean acceptInnerClass() { return false; } 
     @Override public boolean acceptClassName(String name) { 
      return name.startsWith(pkg.getName()) && (includeChildPackages || name.indexOf(".", pkg.getName().length()) != -1); 
     } 
     @Override public boolean acceptClass(Class<?> c) { return true; } 
    }, System.getProperty("java.class.path")).find(); 
} 

的ClasspathClassesFinder查找類文件和罐子在系統類路徑中。

在特定情況下,可以修改acceptClass這樣的:

@Override public boolean acceptClass(Class<?> c) { 
    return ICommand.class.isAssignableFrom(c); 
} 

有一點要注意:要小心你在acceptClassName回報什麼,接下來的事情ClasspathClassesFinder確實被加載類和調用acceptClass 。如果acceptClassName總是返回true,則最終會加載類路徑中的每個類,並可能導致OutOfMemoryError。

0

你可以使用OpenPojo和做到這一點:

final List<PojoClass> pojoClasses = PojoClassFactory.getPojoClassesRecursively("my.package.path", null); 

然後你可以在列表上,並執行任何你想要的功能。