2010-05-31 11 views
59

Tengo el nombre de clase almacenado en un archivo de propiedades. Sé que la tienda de clases implementará IDynamicLoad. ¿Cómo instalo la clase dinámicamente?¿Cómo compilo y crea una instancia de una clase de Java mediante programación?

Ahora mismo tengo

 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(); 

¿El newInstance única carga compilado .class archivos? ¿Cómo cargo una clase de Java que no está compilada?

Respuesta

118

¿Cómo cargo una clase de Java que no está compilada?

Es necesario compilarlo primero. Esto puede hacerse programáticamente con el javax.tools API. Esto solo requiere que el JDK se instale en la máquina local en la parte superior de JRE.

Aquí hay un ejemplo básico patada de salida (dejando el manejo de un lado excepción obvia):

// 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]". 

que da como

hello 
world 
[email protected] 

El uso adicional sería más fácil si esas clases implements un determinado interfaz que ya está en el classpath.

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

De lo contrario, la necesidad de involucrar a Reflection API acceder a e invocar los métodos (desconocido)/campos.


Dicho esto y sin relación con el problema real:

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

Dejar java.io.File se basan en el directorio de trabajo actual es la receta para la portabilidad problemas. No hagas eso. Coloque ese archivo en classpath y use ClassLoader#getResourceAsStream() con una ruta de acceso relativa a la clase.

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

Sólo una pregunta fuera de tema. Obtengo un valor nulo cuando cargo las propiedades a su manera pero obtengo las propiedades cuando lo hago Foo.class.getResourceAsStream()? ¿Podrías ayudarme a entender tu código? Gracias. – unj2

+0

Ese archivo de propiedades aparentemente se coloca en el mismo paquete que la clase 'Foo'. Como se dijo, debe especificar una ruta relativa a la ruta de clase, p. 'com/example/filename.properties'. Pero si puede garantizar que el archivo de propiedades esté siempre en el mismo paquete que la clase 'Foo', entonces' Class # getResourceAsStream() 'también está bien. Solo perderá la capacidad de externalizar el archivo de propiedades fuera de la aplicación para que pueda modificarse sin modificar/reempaquetar la aplicación. – BalusC

+0

'Files.write (source, sourceFile, StandardCharsets.UTF_8);' no compila para mí. Creo que quieres 'Files.write (sourceFile.toPath(), source.getBytes());'. –

4

Tu código comentado es correcto si sabes que la clase tiene un constructor público no-arg. Solo tiene que emitir el resultado, ya que el compilador no puede saber que la clase implementará de hecho IDynamicLoad. Entonces:

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

Por supuesto, la clase tiene que ser compilada y en el classpath para que funcione.

Si está buscando compilar dinámicamente una clase a partir del código fuente, esa es una olla entera de pescado.

5

En la misma línea que la respuesta de BalusC, pero un envoltorio poco más automático está aquí en este pedazo de código de mi distribución kilim. https://github.com/kilim/kilim/blob/master/src/kilim/tools/Javac.java

Toma una lista de cadenas que contienen el código fuente de Java, extrae los nombres del paquete y de la clase pública/interfaz y crea la jerarquía del directorio/archivo correspondiente en un directorio tmp. A continuación, ejecuta el compilador de Java en él y devuelve una lista de nombres, pares de archivos de clases (la estructura ClassInfo).

Regrese al código. Está licenciado por MIT.

+0

¿Qué sucede si la compilación falla? –

Cuestiones relacionadas