2010-05-31 11 views
59

Mam nazwę klasy zapisaną w pliku właściwości. Wiem, że sklep klas wdroży IDynamicLoad. Jak dynamicznie utworzyć instancję klasy?Jak programowo skompilować i utworzyć instancję klasy Java?

Teraz mam

 Properties foo = new Properties(); 
    foo.load(new FileInputStream(new File("ClassName.properties"))); 
    String class_name = foo.getProperty("class","DefaultClass"); 
    //IDynamicLoad newClass = Class.forName(class_name).newInstance(); 

Czy newInstance ładować tylko skompilowane pliki .class? Jak załadować klasę Java, która nie jest skompilowana?

Odpowiedz

118

Jak załadować klasę Java, która nie jest skompilowana?

Najpierw musisz go skompilować. Można to zrobić programowo za pomocą javax.tools API. Wymaga to tylko zainstalowania JDK na komputerze lokalnym na górze JRE.

Oto prosty przykład kickoff (pozostawiając oczywisty wyjątek obsługi bok):

// Prepare source somehow. 
String source = "package test; public class Test { static { System.out.println(\"hello\"); } public Test() { System.out.println(\"world\"); } }"; 

// Save source in .java file. 
File root = new File("/java"); // On Windows running on C:\, this is C:\java. 
File sourceFile = new File(root, "test/Test.java"); 
sourceFile.getParentFile().mkdirs(); 
Files.write(sourceFile.toPath(), source.getBytes(StandardCharsets.UTF_8)); 

// Compile source file. 
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 
compiler.run(null, null, null, sourceFile.getPath()); 

// Load and instantiate compiled class. 
URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] { root.toURI().toURL() }); 
Class<?> cls = Class.forName("test.Test", true, classLoader); // Should print "hello". 
Object instance = cls.newInstance(); // Should print "world". 
System.out.println(instance); // Should print "[email protected]". 

co daje jak

hello 
world 
[email protected] 

Dalsze stosowanie byłoby łatwiej gdyby te zajęcia implements pewien interfejs, który jest już w ścieżce klas.

SomeInterface instance = (SomeInterface) cls.newInstance(); 

W przeciwnym razie trzeba angażować Reflection API dostęp i powoływać się na (brak danych) metody/pola.


Powiedział i niezwiązane z rzeczywistym problemem:

properties.load(new FileInputStream(new File("ClassName.properties"))); 

Letting java.io.File polegać na bieżącym katalogu roboczym jest recepta na przenośności kłopoty. Nie rób tego. Umieść ten plik w ścieżce klas i użyj ClassLoader#getResourceAsStream() ze ścieżką względną clashspath.

properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("ClassName.properties")); 
+0

Tylko nie na temat. Otrzymuję wartość null, gdy ładuję właściwości na swój sposób, ale otrzymuję właściwości, gdy robię to Foo.class.getResourceAsStream()? Czy możesz pomóc mi zrozumieć twój kod? Dziękuję Ci. – unj2

+0

Ten plik właściwości jest najwyraźniej umieszczony w tym samym pakiecie co klasa 'Foo'. Jak już wspomniano, musisz podać ścieżkę względną classpath, np. 'com/example/filename.properties'. Ale jeśli możesz zagwarantować, że plik właściwości jest zawsze w tym samym pakiecie co klasa 'Foo', to również' Klasa # getResourceAsStream() 'jest również w porządku. Tęsknisz tylko za możliwością przekazania pliku właściwości poza aplikację, aby można było go modyfikować bez modyfikowania/przepakowywania aplikacji. – BalusC

+0

'Files.write (source, sourceFile, StandardCharsets.UTF_8);' nie kompiluje się dla mnie. Myślę, że chcesz 'Files.write (sourceFile.toPath(), source.getBytes());'. –

4

Twój skomentowany kod jest poprawny, jeśli wiesz, że klasa ma publiczny konstruktor no-arg. Trzeba tylko rzucić wynik, ponieważ kompilator nie może wiedzieć, że klasa faktycznie wdroży IDynamicLoad. Więc:

IDynamicLoad newClass = (IDynamicLoad) Class.forName(class_name).newInstance(); 

Oczywiście klasa musi być skompilowany i na ścieżce klasy za to do pracy.

Jeśli chcesz dynamicznie skompilować klasę z kodu źródłowego, jest to cały inny czajnik.

5

W tym samym duchu, co odpowiedź BalusC, ale nieco bardziej automatyczne opakowanie jest tutaj w tym kawałku kodu z mojej dystrybucji kilim. https://github.com/kilim/kilim/blob/master/src/kilim/tools/Javac.java

Zawiera listę ciągów zawierających źródło Java, wyodrębnia pakiet i publiczne nazwy klas/interfejsów i tworzy odpowiednią hierarchię katalogów/plików w katalogu tmp. Następnie uruchamia na nim kompilator java i zwraca listę nazw, par plików klas (struktura ClassInfo).

Skorzystaj z kodu. Jest licencjonowany przez MIT.

+0

Co się stanie, jeśli kompilacja się nie powiedzie? –