reports - Configuración de la versión Java de Maven ignorada por Eclipse/Idea
surefire pom (1)
Yo tengo:
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
Sin embargo, no tengo problemas para declarar:
public enum DirectoryWatchService {
INSTANCE;
private java.util.Optional<String> test;
private java.nio.file.Files files;
}
Eclipse no molesta. IntelliJ tampoco. Incluso Maven no se molesta. Incluso puedo hacer un paquete limpio mvn . Construye la maldita cosa sin ninguna advertencia.
Estás llegando al malentendido de compilación cruzada de
target
opciones de
source
/
target
.
Usando una versión principal en tu classpath (JDK 7 u 8) pero deseando compilar contra una versión menor (6 en tu caso).
La compilación estará bien, pero tendrá errores en
tiempo
de
ejecución
(es decir,
NoClassDefFoundError
o
NoSuchMethodError
, probablemente también el
LinkageError
más genérico).
Usando el
source
/
target
, el compilador de Java puede usarse como un compilador cruzado para producir archivos de clase ejecutables en JDK que implementan una versión anterior de la especificación Java SE.
La creencia común es que usar dos opciones de compilación sería suficiente.
Sin embargo, la opción de
source
especifica contra qué versión estamos compilando, mientras que la opción de
target
especifica la versión de Java más baja para admitir.
El compilador funciona en la generación de bytecode y la
source
y el
target
se utilizan para generar bytecode compatible durante la compilación cruzada.
Sin embargo, el compilador no maneja las API de Java (se proporcionan como parte de la instalación de JDK, el famoso archivo
rt.jar
).
El compilador no tiene ningún conocimiento de API, solo compila contra el
rt.jar
actual.
Por lo tanto, al compilar con
target=1.6
utilizando JDK 1.7, el compilador seguirá apuntando a JDK 7
rt.jar
.
Entonces, ¿cómo podemos tener una compilación cruzada correcta?
Desde JDK 7, javac imprime una advertencia
en caso de que el
source
/
target
no esté en combinación con la opción
bootclasspath
.
La opción
bootclasspath
es la opción clave en estos casos para apuntar al
rt.jar
de la versión de Java de destino deseada (por lo tanto, debe tener el JDK de destino instalado en su máquina).
Como tal,
javac
compilará efectivamente contra la buena API de Java.
¡Pero eso puede no ser suficiente!
No todas las API de Java provienen de
rt.jar
.
La carpeta
lib/ext
proporciona otras clases.
Para eso se usa una opción adicional de
javac
,
extdirs
.
De documentos oficiales de Oracle
Si realiza una compilación cruzada (compilando clases contra bootstrap y clases de extensión de una implementación de plataforma Java diferente), esta opción especifica los directorios que contienen las clases de extensión.
Por lo tanto, incluso usando las opciones
source
/
target
y
bootclasspath
, aún podemos perder algo durante la compilación cruzada, como también se explica en el
ejemplo oficial
que viene con la documentación de javac.
Java Platform JDK de Java también compilaría por defecto contra sus propias clases de bootstrap, por lo que debemos decirle a javac que compile contra JDK 1.5. Hacemos esto con -bootclasspath y -extdirs. No hacerlo puede permitir la compilación contra una API de plataforma Java que no estaría presente en una máquina virtual 1.5 y fallaría en tiempo de ejecución.
Pero ... aún puede no ser suficiente!
De la documentation oficial de Oracle
Incluso cuando bootclasspath y -source / -target están configurados adecuadamente para la compilación cruzada, los contratos internos del compilador, como la forma en que se compilan las clases internas anónimas, pueden diferir entre, por ejemplo, javac en JDK 1.4.2 y javac en JDK 6 corriendo con la opción -target 1.4.
La solución sugerida (de la documentación documentation )
La forma más confiable de producir archivos de clase que funcionarán en un JDK particular y más tarde es compilar los archivos fuente utilizando el JDK de interés más antiguo. Salvo eso, el bootclasspath debe estar configurado para una compilación cruzada robusta a un JDK anterior.
Entonces, ¿esto realmente no es posible?
Spring 4 actualmente es compatible con Java 6, 7 y 8 . Incluso usando las características y API de Java 7 y Java 8. ¿Cómo puede ser compatible con Java 7 y 8?
Spring hace uso de la flexibilidad
source
/
target
y
bootclasspath
.
Spring 4 siempre compila con
source
/
target
para Java 6, de modo que el
source
bytes aún puede ejecutarse bajo JRE 6. Por lo tanto, no se utilizan características de lenguaje Java 7/8: la sintaxis mantiene el nivel de Java 6.
¡Pero también usa Java 7 y Java 8 API!
Por lo tanto, la opción
bootclasspath
no se usa.
Opcional, se utilizan Stream y muchas otras API de Java 8.
Luego inyecta beans dependiendo de la API de Java 7 o Java 8 solo cuando se detectan JRE 7/8 en tiempo de ejecución: ¡enfoque inteligente!
Pero, ¿cómo asegura Spring la compatibilidad de la API entonces?
Usando el complemento
Maven Animal Sniffer
.
Este complemento verifica si su aplicación es compatible con API con una versión específica de Java.
Se llama sniffer de animales porque Sun tradicionalmente nombra las diferentes versiones de Java después de
diferentes animales
(Java 4 = Merlin (pájaro), Java 5 = Tigre, Java 6 = Mustang (caballo), Java 7 = Delfín, Java 8 = ningún animal).
Puede agregar lo siguiente a su archivo POM:
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>animal-sniffer-maven-plugin</artifactId>
<version>1.14</version>
<configuration>
<signature>
<groupId>org.codehaus.mojo.signature</groupId>
<artifactId>java16</artifactId>
<version>1.0</version>
</signature>
</configuration>
<executions>
<execution>
<id>ensure-java-1.6-class-library</id>
<phase>verify</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Y la compilación fallará tan pronto como use JDK 7 API mientras desea usar solo JDK 6 API.
Este uso también se recomienda en la página oficial de
source
/
target
del
maven-compiler-plugin
Nota : el simple hecho de configurar la opción de
target
no garantiza que su código realmente se ejecute en un JRE con la versión especificada. La trampa es el uso no intencionado de API que solo existen en JRE posteriores, lo que haría que su código fallara en tiempo de ejecución con un error de enlace. Para evitar este problema, puede configurar la ruta de clase de arranque del compilador para que coincida con el JRE de destino o usar el complemento Animal Sniffer Maven para verificar que su código no use API no deseadas.
Actualización de Java 9
En Java 9, este mecanismo
ha cambiado radicalmente
al siguiente enfoque:
javac --release N ...
Será semánticamente equivalente a
javac -source N -target N -bootclasspath rtN.jar ...
Información sobre las API de versiones anteriores disponibles para
javac
- Almacenado de manera comprimida
- Solo proporcione API exportadas Java SE N y JDK N que sean neutrales a la plataforma
- El mismo conjunto de valores de lanzamiento N que para
-source
/-source
- Combinaciones incompatibles de opciones rechazadas
Principales ventajas del enfoque
--release N
:
- Ningún usuario necesita administrar artefactos que almacenan información API antigua
- Debe eliminar la necesidad de usar herramientas como el complemento Maven Animal Sniffer
Puede usar modismos de compilación más nuevos que
javac
en versiones anteriores
- Corrección de errores
- Mejoras de velocidad
Actualización sobre Java 9 y Maven
Desde la versión
3.6.0
, el
maven-compiler-plugin
proporciona soporte para Java 9 a través de su opción de
release
:
El argumento de
-release
para el compilador de Java, compatible desde Java9
Un ejemplo:
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<release>8</release>
</configuration>
</plugin>