W3docs

Tipos de módulos en Java

Módulos con nombre, automáticos y sin nombre en Java, y cómo interactúan durante la compilación y la ejecución.

El Java Platform Module System (JPMS) reconoce tres tipos de módulo. Solo uno es el "real" que tú creas; los otros dos existen para que los millones de JARs anteriores a Java 9 sigan funcionando. Entender en qué tipo se convierte un JAR determinado — y que depende por completo de dónde lo coloca — es la clave para migrar sin problemas. Esta página define los tres tipos, muestra las reglas de acceso entre ellos y demuestra las categorías con un programa ejecutable.

Módulos con nombre

Un módulo con nombre (explícito) es aquel que tiene un module-info.class, colocado en la ruta de módulos (--module-path / -p). Es el ciudadano de pleno derecho:

  • Tiene un nombre proveniente de su descriptor.
  • Lee únicamente los módulos que declara con requires.
  • Expone únicamente los paquetes que declara con exports.

Este es el módulo con encapsulación fuerte que describe el capítulo sobre la declaración de módulos. Todo lo que JPMS promete — dependencias declaradas, internos ocultos, resolución fallida anticipada — aplica a los módulos con nombre.

Módulos automáticos

Un módulo automático es un JAR ordinario (sin module-info) colocado en la ruta de módulos. JPMS lo envuelve en un módulo para que los módulos con nombre puedan declararlo con requires durante la migración — sin esperar a que el autor de la biblioteca añada un descriptor. Un módulo automático:

  • Obtiene un nombre derivado del nombre del archivo JAR (por ejemplo, guava-32.1.jarguava), a menos que el manifiesto del JAR establezca Automatic-Module-Name.
  • Exporta todos sus paquetes — no tiene directiva exports, por lo que todos sus paquetes están abiertos al mundo.
  • Lee todos los demás módulos, incluido el módulo sin nombre, para que pueda seguir viendo los JARs del classpath.

Es un puente: permite comenzar a escribir módulos con nombre que dependan de bibliotecas aún no modularizadas. El coste es que renuncia por completo a la encapsulación, y su nombre derivado automáticamente puede cambiar si se renombra el JAR — por eso Automatic-Module-Name en el manifiesto es lo responsable que una biblioteca debería incluir.

El módulo sin nombre

El módulo sin nombre es el comodín para el classpath. Cada clase cargada desde el classpath pertenece al módulo sin nombre de su cargador de clases. Este módulo:

  • No tiene nombre (getName() devuelve null, isNamed() es false).
  • Lee todos los demás módulos del sistema.
  • Exporta todos sus paquetes a otros módulos sin nombre/automáticos.

Pero existe una barrera unidireccional deliberada: un módulo con nombre no puede declarar requires del módulo sin nombre. No puedes nombrarlo, por lo que no puedes depender de él. Esta es la regla que impone el orden de migración — un módulo con nombre solo puede depender de otros módulos con nombre o automáticos, nunca de código en el classpath puro.

La matriz de acceso

Quién puede leer a quién se resume en una pequeña tabla:

De ↓ / A →Con nombreAutomáticoSin nombre
Con nombresolo si requiressolo si requiresnunca
Automático
Sin nombre

La única celda restrictiva — el código con nombre no puede acceder al código sin nombre — es la razón completa de por qué se migra de abajo hacia arriba (cubierto en el siguiente capítulo).

Un ejemplo práctico: identificar el tipo de un módulo en tiempo de ejecución

La API Module te indica, para cualquier clase, si su módulo tiene nombre y si fue sintetizado automáticamente. Este programa inspecciona tres referencias — su propia clase (classpath → sin nombre), un tipo del JDK (con nombre) y reporta la capa de arranque — para hacer concretas las categorías.

java— editable, runs on the server

Lo que se puede deducir de la ejecución:

  • La propia clase del programa se clasificó como UNNAMED con nombre null, mientras que java.util.List y HttpClient se clasificaron como NAMED (java.base, java.net.http). Al ejecutarse desde el classpath, tu código siempre es sin nombre; el JDK siempre es un conjunto de módulos con nombre. El tipo de un módulo lo determina cómo fue cargado, no nada que esté en la clase en sí.
  • java.base.canRead(self) devolvió false pero self.canRead(java.base) devolvió true. Esa es la barrera unidireccional en acción: el módulo sin nombre lee todo, pero ningún módulo con nombre lee el módulo sin nombre. Esta asimetría es precisamente por qué el código con nombre no puede declarar requires de código del classpath.
  • classify() distinguió automático de con nombre mediante descriptor.isAutomatic(). No verás true aquí (nada fue colocado en la ruta de módulos como JAR simple), pero la comprobación es exactamente cómo las herramientas reportan un módulo automático — un objeto de módulo real con un descriptor sintetizado y completamente abierto.
  • isExported("java.util") devolvió true pero isExported("jdk.internal.misc") devolvió false, aunque ambos son paquetes reales dentro de java.base. Los exports de un módulo con nombre son una lista de permitidos; los paquetes no exportados (o solo exportados de forma calificada) son invisibles para el código externo sin importar que sean public. El módulo sin nombre, en cambio, exporta todo lo que contiene.
  • No se necesitó ningún module-info.java para observar nada de esto. Las tres categorías son hechos en tiempo de ejecución sobre cómo se cargó una clase, y getModule() junto con getDescriptor() los exponen — las mismas llamadas en que se apoyan las herramientas de migración para determinar con qué están trabajando.

Por qué existen tres tipos

Los dos tipos de compatibilidad — automático y sin nombre — permiten que Java 9+ ejecute aplicaciones Java 8 sin modificar. Optas por la encapsulación fuerte un JAR a la vez: deja todo en el classpath (todo sin nombre) y nada cambia; mueve una biblioteca a la ruta de módulos sin descriptor y se vuelve automática; añade un module-info.java y se vuelve con nombre. A continuación, los servicios de módulos muestran el mecanismo uses/provides que desacopla los módulos, y la migración de módulos lleva un proyecto real a través de estos tres estados.

Práctica

Práctica
Durante la migración colocas un JAR de terceros (sin 'module-info.class') llamado 'fastjson-2.0.jar' en la RUTA DE MÓDULOS, luego escribes 'requires fastjson;' en tu propio módulo con nombre. ¿Qué afirmación describe correctamente 'fastjson' en este caso?
Durante la migración colocas un JAR de terceros (sin 'module-info.class') llamado 'fastjson-2.0.jar' en la RUTA DE MÓDULOS, luego escribes 'requires fastjson;' en tu propio módulo con nombre. ¿Qué afirmación describe correctamente 'fastjson' en este caso?
Was this page helpful?