2010-02-01 11 views
16

Por lo tanto, cada libro de texto de Java habla sobre la flexibilidad de Java, ya que puede cargar clases en tiempo de ejecución. Simplemente improvisar una cadena y dársela al Class.forName(), y atrapar el ClassNotFoundException y manejarlo. Hasta aquí la teoría.¿Para qué usa la gente la carga de clases?

¿Puede dar ejemplos de cómo ha estado utilizando la carga de clase Java para lograr una característica que de otro modo no hubiera sido posible o fácil? Tenga en cuenta que estoy no preguntando "¿Qué cosas tan buenas podría que hacemos?" - Estoy buscando ejemplos del mundo real, ya sea una aplicación de código abierto o, si puede describir esto sin dar demasiados detalles, una aplicación patentada.

Edit: Claro, el VM carga las clases perezosamente cuando las necesita. Eso es algo detrás de escena siempre y cuando esté seguro de que todas las clases que alguna vez necesitaré están ahí. ¿Cómo manejo un ClassNotFoundException? Supongamos que he escrito diez páginas de texto y no se puede encontrar la clase PrinterDriver.

+0

Hibernate y AspectJ son ejemplos de creación de clases sobre la marcha. –

Respuesta

10

Plugins es lo primero que viene a la mente. La carga de la clase Java hace que sea muy fácil en comparación con idiomas como C++.

Un punto que tal vez desconozca es que cualquier máquina virtual Java depende en gran medida de la carga de clases internamente. Cada vez que el intérprete de códigos de bytes ve una referencia, por ejemplo, a un método, comprueba si la clase a la que pertenece el método ya está cargada, y si no lo está, la carga utilizando el mismo mecanismo detrás de Class.forName() antes de resolver el método. Este mecanismo es muy poderoso ya que cualquier aplicación Java realmente actúa como un conjunto de componentes reemplazables que están cargados dinámicamente. Si la máquina virtual está bien escrita, puede, por ejemplo, cargar clases a través de un cargador de clases personalizado que recupera las clases de la red en lugar de los archivos normales.

El tiempo de carga de clase depende de la implementación de la máquina virtual, pero la mayoría depende de este mecanismo de enlace tardío que carga una clase la primera vez que la VM la encuentra.

+0

Bueno, GKrellM, implementado en C, funciona bien con los plugins. Póngalos en su directorio ~/.gkrellm2/plugins/y reinicie GKrellM para que los encuentre (también podría buscar nuevos complementos cada vez que se abra el cuadro de diálogo de configuración). – doppelfish

+0

Ciertamente, mi punto es que implementar complementos utilizando la carga de clase es mucho más fácil y portátil que usar la carga de biblioteca dinámica tradicional utilizada por los programas C y C++. No estoy tratando de decir que le permite hacer más que en C (y sí, ¡GKremmM lo hace genial!). – Gnurou

3

Bueno, lo he usado para cargar dinámicamente los controladores JDBC en una aplicación J2EE. Wheteher esto podría haber sido hecho de una mejor manera, no tengo ni idea.

Simplemente fue más fácil en ese momento hacer la llamada forName().

+0

En realidad, nunca he visto otra forma de obtener un controlador JDBC. Hay debe ser una mejor manera, pero no lo he visto. De lujo que. – doppelfish

+1

Una alternativa es establecer el controlador en la propiedad del sistema jdbc.drivers. –

0

Lo uso cuando estoy construyendo una aplicación comercial que debe ser adaptada por mí o por el cliente para cumplir con los requisitos específicos del cliente.

2

Estoy bastante seguro de que la carga del plugin en Java depende mucho de eso.

la aplicación comprueba funciones con nombre y los ejecuta

Eclipse actually uses that for plugins

La idea clave es la ejecución de código que no estaba prevista en el momento Developpement.

2

Puede ser extremadamente útil en situaciones en las que utiliza una API y los diseñadores de la API realmente desaprobaron algunas clases de una versión a la siguiente (por ejemplo, Contacts en Android).

Sin reflexión y carga dinámica de clases basada en el nombre de la cadena, sería imposible en este caso ejecutar el mismo programa en ambas versiones de la plataforma sin obtener una excepción de clase no encontrada en el tiempo de ejecución. Pero con eso, el mismo programa se modificó un poco y luego pudo ejecutarse en ambas plataformas.

5

"PLUGIN" y esa es la gran palabra.

Básicamente, puede cargar una clase que no sabe cuándo o no cuando escribe y compila su programa.

Por ejemplo, si desea que un programa haga un corrector ortográfico, puede escribir una interfaz SpellChecker y luego cargar una clase desde un archivo de configuración que implementa la interfaz SpellChecker. Después de eso, puede escribir cualquier SpellChecker y establecer en el archivo de configuración el nombre real del archivo. De esta forma, su programa no necesita saber qué clase hará la corrección ortográfica.

Controlador de base de datos, plugin de Eclipse, lenguaje de secuencia de comandos, métodos de criptografía están hechos de esta manera ya que el escritor original no sabe (y en algunos casos, no tiene idea) qué clase realmente se utilizará.

Espero que esto ayude.

2

El cargador de clases también se usa para recursos que no son de clase. Los archivos de configuración vienen a la mente. Como hay un orden de búsqueda bien definido, es fácil ingresar su propio "log4j.xml" o "hibernate.properties", y la aplicación lo encontrará y lo usará.

1

creo que JUnit también puede usar muchas funciones de reflexión para hacer genérico el marco de prueba.

-2

Puede utilizar el método Class :: forName si la clase está en la ruta de la clase. Sin embargo, si necesita dar una ruta junto con el nombre de la clase, es decir c: \ document \ xyz.class, tendrá que usar la clase URLClassLoader.

+1

La respuesta no es incorrecta como tal, pero realmente no responde la pregunta. –

0

Consulte la compatibilidad con Oracle LOB handling en Spring Framework, por ejemplo. Solo porque el marco ofrece soporte específico para Oracle probablemente no desee implementar un origen de datos de Oracle como dependencia para su p. Ej. Proyectos MySQL. Por lo tanto, carga los controladores de Oracle de forma reflexiva en el ámbito de una instancia del manejador de LOB.

5

Los servidores de aplicaciones también se basan en gran medida en ClassLoaders para aislar los diferentes módulos desplegados. P.ej.

  • puede implementar la misma aplicación web dos veces bajo diferentes ruta
  • dos aplicaciones puede depender de dos versiones diferentes de la misma biblioteca sin conflicto.

, gracias a la magia de los cargadores de clases ...

+0

Es cierto, pero (1) puedo iniciar el mismo binario dos veces, cada vez con una configuración diferente, y (2) puedo tener una versión diferente de un dll (objeto compartido) y los binarios pueden vincular con su versión favorita. Lo siguiente que sabes es el infierno. – doppelfish

0

contenedores de servlets como Tomcat leer su archivo de configuración de la guerra/webapp de WEB-INF/web.xml y cargar el servlet/Filtro/etc. subclases basadas en los valores de cadena que coloca en el archivo XML. Para las conexiones a bases de datos, extraen el nombre de la clase para cargar desde su configuración, p. "com.mysql.jdbc.Driver" para MySQL.

2

Recuerdo haber creado un cargador de clases para cargar clases de forma remota. La aplicación se estaba ejecutando en un nodo mientras las clases estaban almacenadas en otro nodo.

Y también al personalizar el cargador de clases puede transformar las clases a medida que se cargan. Esto es utilizado por algunos marcos ORM, así como algunos marcos AOP.

0

Ejemplo del mundo real (como se solicita en su pregunta), aplicación propietaria (como lo permite explícitamente su pregunta) ...

Al iniciarse, el software del lado del cliente contacta con nuestro servidor y dice "La implementación predeterminada de la barra de interfaz que tengo es Foo (porque en realidad cada versión 1.03, por ejemplo, usa Foo), ¿tiene una mejor ¿uno?" Si mientras tanto escribimos una mejor implementación, respondemos "sí, Bar es viejo, usa Buz, es mejor".

Luego, en el lado del cliente, se utiliza un cargador de clases para cargar la última implementación.

Se simplifica demasiado, pero es un ejemplo del mundo real. No es del todo diferente al ejemplo que JRL mencionó: donde las clases en desuso se reemplazan automágicamente por las más nuevas.

2

La API JDBC es un excelente ejemplo de esto. De esta manera se puede configurar el controlador JDBC en el exterior, por ejemplo, un archivo de propiedades:

 
driver = com.dbvendor.jdbc.Driver 
url = jdbc:dbvendor://localhost/dbname 
username = stackoverflow 
password = youneverguess 

..which se puede utilizar como:

Properties properties = new Properties(); 
properties.load(Thread.currentThread().getResourceAsStream("jdbc.properties")); 

String driver = properties.getProperty("driver"); 
String url = properties.getProperty("url"); 
String username = properties.getProperty("username"); 
String password = properties.getProperty("password"); 

Class.forName(driver); 
Connection connection = DriverManager.getConnection(url, username, password); 

Cada controlador JDBC aplicación básicamente registra a sí mismo en el interior de un DriverManagerstatic bloque inicializador. Es el que se ejecuta durante Class#forName().

package com.dbvendor.jdbc; 

public class Driver implements java.sql.Driver { 

    static { 
     java.sql.DriverManager.registerDriver(new Driver()); 
    } 

    private Driver() { 
     // ... 
    } 

    public boolean acceptsURL(String url) { 
     return url.startsWith("jdbc:dbvendor"); 
    } 

} 

Desde el DriverManager más o menos el siguiente aspecto (que en realidad utiliza la antigua usanza Vector)

private static final Set<Driver> drivers = new HashSet<Driver>(); 

public static void registerDriver(Driver driver) { 
    drivers.add(driver); 
} 

public static Connection getConnection(String url, String username, String password) throws SQLException { 
    for (Driver driver : drivers) { 
     if (driver.acceptsURL(url)) { 
      return driver.connect(url, username, password); 
     } 
    } 
    throw new SQLException("No suitable driver"); 
} 

... se puede conseguir una conexión de ella sin la necesidad de crear una instancia del propio controlador !

De esta forma, el código JDBC es altamente portátil. Puede cambiar el DB o distribuir el código entre usuarios con diferentes DB sin la necesidad de cambiar/piratear/reconstruir el código en sí.

No es solo JDBC el que utiliza este enfoque, también otras API como Servlet API, ORM como Hibernate/JPA, marcos de inyección de dependencia, etcétera usa la reflexión para cargar las clases en archivos configurables externamente, archivos de configuración XML y/o anotaciones Todo hace que el código sea mucho más portátil y conectable.

1

El mecanismo cargador de clases de Java es de gran alcance, ya que proporciona un punto de extracción exactamente en el punto en el que se carga el código, lo que le permite hacer cosas tales como:

  • encontrar los bits de clase algún lugar que la ruta de clase (db , URL remoto, sistema de archivos, etc.)
  • código fuente de carga que acaba de crear y usted mismo compilado (con la API javac)
  • carga el código de bytes que acaba de generar usted mismo (digamos con ASM)
  • código de carga y modificándolo antes de usar i t (con ASM, agentes de Java, etc, etc)
  • código RE-carga sobre la marcha
  • cargadores encadenar en árboles (delegación normal) o napa (estilo OSGi basadas en hermanos) o como se quiera

A punto de modificar el código durante la carga, hay un mundo de cosas interesantes que puedes hacer para volver a mezclar tu código: AOP, perfilado, seguimiento, modificaciones de comportamiento, etc. En Terracotta confiamos en la abstracción del cargador de clases para cargar dinámicamente una clase , luego intercepte todo el acceso a los campos y agregue dinámicamente la capacidad de cargar el estado desde el mismo objeto en un nodo remoto en el clúster más adelante. Cosas interesantes.

0

El uso de la clase dinámica de carga también es muy útil para cargar archivos de configuración como menciona Thilo. En términos más generales, la carga dinámica de clases puede hacer una buena capa de abstracción del sistema de archivos en muchas situaciones, simplificando la preferencia de escritura y el código sensible a la configuración. Solo asegúrate de que el recurso que necesitas esté en el classpath y cárgalo como un InputStream.

Además, con un controlador de protocolo personalizado en Java es posible acceder a los elementos en el classpath a través de la URL. Esta no es una ventaja específica de la carga dinámica de clases, pero sí demuestra cómo se puede acceder a los recursos de classpath a través de la misma API que otros recursos (incluso los remotos). http://java.sun.com/developer/onlineTraining/protocolhandlers/

0

Cualquier estructura que esté basada en la configuración (puntales, jsf, resorte, hibernación, etc.) utiliza este mecanismo. Cualquier producto que esté basado en la arquitectura del complemento también usa esta característica.

Cuestiones relacionadas