Antecedentes:
Tengo una clase de prueba principal que tiene muchos botones, cada uno de los cuales ejemplifica una clase que estoy codificando/probando. Quiero que el código/ciclo de prueba para estas clases sea rápido y vea el efecto de mis cambios rápidamente, algunas veces por minuto. MainTest, que es estable, tarda unos 20 segundos en cargarse, lo que no sería un problema si no hubiera necesitado volver a cargarlo para cada cambio en las clases en las que crea una instancia. Quiero cargar MainTest una vez, y cuando crea otra clase, llamémoslo ChildTest, en numerosas ocasiones (tras el evento del botón), debería volver a cargar la versión más reciente de ChildTest.
La pregunta en resumen:
¿Cómo se le dice al comando java 'new' que vuelva a cargar la clase desde el disco y no desde el caché de JVM?
Intenté Class.ForName pero no hizo la diferencia.
También he intentado utilizar un cargador de clases personalizado (copiado de código abierto), en vano.¿Cómo forzar a Java a volver a cargar la clase al crear instancias?
Respuesta
No hay ninguna posibilidad de "sobrecargar" al operador new
, pero ciertamente podría escribir un cargador de clases personalizado que simplemente vuelva a cargar el bytecode cada vez que le pida que cargue una clase. Ningún cargador de clases listo para usar hará lo que está buscando porque todos suponen que la definición de clase no cambiará a lo largo de la vida de la JVM.
Pero así es cómo lo haces realidad. Cree un cargador de clases llamado, por ejemplo, Reloader
que anula los métodos loadClass
y findClass
para que simplemente recarguen los archivos de clase del disco cada vez que son llamados (en lugar de "almacenarlos en caché" para usarlos posteriormente). Luego solo tiene que llamar al new Reloader().loadClass("foo.bar.MyClassName")
cada vez que sospeche que la definición de la clase ha cambiado (por ejemplo, como parte de los métodos del ciclo de vida de su sistema de prueba).
This article rellena algunos de los detalles pero omite algunos puntos importantes, especialmente sobre el uso de nuevas instancias del cargador de clases para las recargas posteriores y la delegación al cargador de clases predeterminado cuando corresponda. Aquí es un simple ejemplo de trabajo que carga en repetidas ocasiones la clase MyClass
y asume su archivo de clase existe en el directorio relativa "./bin":
public class Reloader extends ClassLoader {
public static void main(String[] args) throws Exception {
do {
Object foo = new Reloader().loadClass("MyFoo").newInstance();
System.out.println("LOADED: " + foo); // Overload MyFoo#toString() for effect
System.out.println("Press <ENTER> when MyFoo.class has changed");
System.in.read();
} while (true);
}
@Override
public Class<?> loadClass(String s) {
return findClass(s);
}
@Override
public Class<?> findClass(String s) {
try {
byte[] bytes = loadClassData(s);
return defineClass(s, bytes, 0, bytes.length);
} catch (IOException ioe) {
try {
return super.loadClass(s);
} catch (ClassNotFoundException ignore) { }
ioe.printStackTrace(System.out);
return null;
}
}
private byte[] loadClassData(String className) throws IOException {
File f = new File("bin/" + className.replaceAll("\\.", "/") + ".class");
int size = (int) f.length();
byte buff[] = new byte[size];
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
dis.readFully(buff);
dis.close();
return buff;
}
}
En cada invocación del 'do/while' bloque en el principal método , se crea una instancia de un nuevo Reloader que carga la clase desde el disco y la devuelve a la persona que llama. Por lo tanto, si sobrescribe el archivo bin/MyClass.class
para contener una nueva implementación con un método diferente y sobrecargado toString
, entonces debería ver la nueva implementación cada vez.
Actualmente estoy trabajando con el CustomClassLoader de ese artículo, pero tengo problemas para lograr que haga lo que quiero. Suena como el enfoque correcto, pero tendré que profundizar en el artículo, supongo. – nat101
Tendrá que tener cuidado de no utilizar el operador "nuevo" para la clase cambiante o de lo contrario se usará el cargador de clases del sistema predeterminado en lugar del suyo. Intente utilizar algo como esto en su lugar 'MyClass foo = myReloader.loadClass (" MyClass "). NewInstance();' – maerics
¡Sí! Hace el trabajo. Incluso para una GUI. Gracias. – nat101
No estoy seguro de cómo hacer esto en el código, pero NetBeans tiene un botón "Aplicar cambios de código" que hará esto.
Sin embargo, solo puede cambiar la implementación de la clase, no puede cambiar su firma. Esto significa que no puede agregar, eliminar o cambiar variables o métodos de instancia. Esto se debe al diseño de la JVM que no permite que se cambien una vez que una clase se ha cargado una vez.
Como NetBeans puede hacerlo, debe haber una forma, pero no sé cómo hacerlo manualmente.
Si ejecuta un IDE que admita esta función, puede hacer clic en el botón cada vez que modifique una clase. Luego recompilará esa clase y la volverá a cargar.
Estoy usando Eclipse, que se compila automáticamente al guardar. La pregunta es cómo cargar la clase recién compilada en un jvm que ya tiene cargada la versión anterior de la misma clase. – nat101
Parece que no quiere utilizar la implementación activa cuando se puede usar con el depurador. Cuando depura un problema y recompone algunas de sus clases, puede obtener la opción de volver a cargar las clases modificadas.
EDITAR: Además de usar la API de depuración, puede usar Instrumentación. http://download-llnw.oracle.com/javase/6/docs/api/java/lang/instrument/package-summary.html
Sin embargo, dado que el uso de un depurador es, de lejos, la forma más sencilla de hacerlo, si esto no funciona, es probable que se encuentre con los mismos problemas.
Suena como lo que necesita para probar piezas de trabajo más pequeñas por lo que lleva menos de un segundo ejecutar algún subconjunto de su aplicación.
O puede cargar su aplicación más rápido al proporcionar una instalación de volcado y recarga para la memoria. De esta manera podría iniciar su aplicación desde una instantánea (quizás de inmediato)
Realmente no puedo usar el depurador para este propósito, demasiado lento. (GUI extenso.) Pero mi pregunta es, si el depurador puede hacerlo, ¿por qué no puedo? – nat101
Puede hacerlo si usa la API de depuración para hacerlo. Sin embargo, esto no es simple de usar. Cambiar el cargador de clases no cambiará una clase ya cargada cambiando su código. ver editar. –
Suena un poco aterrador, pero esto debería ayudar.
http://download.oracle.com/javase/1.4.2/docs/api/java/lang/ClassLoader.html
ClassLoader puede cargar dinámicamente las clases en tiempo de ejecución, me gustaría leer la API para determinar si la carga de nuevo reemplaza la versión anterior.
Tendría que escribir mi propia subclase ClassLoader para lograr esto. – nat101
Esto no sería muy difícil de lograr, simplemente extienda el ClassLoader y elija siempre crear una nueva instancia a partir del archivo fuente. La API proporciona una breve descripción de cómo podría hacer esto para obtener dinámicamente un archivo de una red. Su caso sería de una fuente de archivo constante. – Valchris
Encontré un artículo sobre exactamente este problema.
http://www.zeroturnaround.com/blog/reloading-objects-classes-classloaders/
PERO, la respuesta de maerics también se ve bien. Lo intentaré más tarde.
Puede probar Java Rebel. Es un cargador de clases "solo para el desarrollo" diseñado para hacer exactamente lo que necesita pero, quizás, podría ser costoso para sus necesidades. En tu caso, la solución de Peter Lawrey podría ser suficiente.
- 1. ¿Cómo forzo a ActiveRecord a volver a cargar una clase?
- 2. Cómo forzar a tomcat a volver a cargar los archivos de clase/guerra compilados recientemente
- 3. ¿Cómo forzar a un script a volver a cargar y volver a ejecutar?
- 4. Cómo forzar al objeto Lazy <T> a crear/volver a crear el valor?
- 5. Cómo forzar al rastreador a volver a indexar una carpeta?
- 6. ¿Cómo volver a cargar TreeStore?
- 7. ¿Cómo volver a cargar gdbinit?
- 8. cómo forzar a google a volver a indexar una página
- 9. Cómo forzar a Xcode a volver a vincular
- 10. ¿Cómo volver a cargar un div sin volver a cargar toda la página?
- 11. ¿Cómo puedo forzar a Rails a cargar todos los modelos?
- 12. Obligar a ConfigurationManager a volver a cargar todas las secciones
- 13. ¿Cómo forzar a la clase derivada a llamar al supermétodo? (Como Android lo hace)
- 14. ¿Cómo forzar a una página a volver a cargar si todo lo que se cambió en la url es hash?
- 15. Java: ¿volver a cambiar al hilo principal?
- 16. ¿Volver a cargar hojas de estilo CSS sin volver a cargar la página?
- 17. Forzar una vista para volver a dibujarse
- 18. Cómo forzar a Windows para volver a conectar a la unidad de red
- 19. cómo volver a cargar solo scripts a través de firebug
- 20. Cómo forzar a un método a ser anulado en Java?
- 21. Tiempo de inactividad al volver a cargar mod_wsgi daemon?
- 22. ¿Volver a cargar una sola fila jqGrid?
- 23. ¿Cómo dejar que jetty bloquee la solicitud al volver a cargar las clases modificadas?
- 24. Actualizar/volver a cargar Flot en Javascript
- 25. Cómo forzar a un applet de Java a cargar desde el caché
- 26. ¿Cómo puedo programar Logback para volver a cargar la configuración
- 27. ¿Cómo forzar a moq a llamar al constructor?
- 28. ¿Cómo obtener una UITableview para ir al inicio de la página al volver a cargar?
- 29. ¿Volver a cargar rubygems en irb?
- 30. ¿Cómo forzar a Java a lanzar una excepción aritmética?
¿está utilizando un marco de prueba como JUnit o es un banco de pruebas de cosecha propia? –
clase de prueba de cosecha propia. – nat101