example ejemplo spring-boot jar classloader

spring boot - ejemplo - ¿Por qué los Jars en tarros no pueden ver el contenido de otros tarros en tarros si están en el mismo tarro?



classloader java ejemplo (3)

tl; dr: Las clases en nuestro jar de Spring Boot parecen ver las clases dentro de los frascos empaquetados, pero sus contenidos no parecen ser capaces de hacerlo. ¿Por qué?

Nuestro producto principal es una aplicación web, pero toda la lógica empresarial está centralizada en un núcleo mac-guffin-api.jar . mac-guffin-api.jar no es un proyecto de Spring Boot, pero tiene un archivo de configuración Spring Java llamado net.initech.api.Configuration que inicializa todos los servicios y repositorios, etc. Usamos MS SQL Server como nuestro back-end con el sqljdbc42:jar controlador de sqljdbc42:jar

Necesitábamos escribir un ETL que necesitara reutilizar la misma lógica comercial del proyecto API, por lo que creamos un proyecto Spring Boot Spring Batch que importa mac-guffin-api.jar como una dependencia Maven. La configuración de la configuración de ETL ( net.initech.etl.Configuration ) de las API de importación sin problemas (puedo verlo desde el registro de la consola) pero cuando la configuración de la API va a crear la conexión de la base de datos, no puede encontrar el controlador.

Caused by: java.lang.ClassNotFoundException: ''com.microsoft.sqlserver.jdbc.SQLServerDriver'' at java.net.URLClassLoader.findClass(Unknown Source) at java.lang.ClassLoader.loadClass(Unknown Source) at org.springframework.boot.loader.LaunchedURLClassLoader.loadClass(LaunchedURLClassLoader.java:94) at java.lang.ClassLoader.loadClass(Unknown Source) at java.lang.Class.forName0(Native Method) at java.lang.Class.forName(Unknown Source) at org.apache.tomcat.jdbc.pool.PooledConnection.connectUsingDriver(PooledConnection.java:246) ... 113 more

Sin embargo, puedo ver claramente que el JAR que contiene el controlador está presente. Los contenidos del mac-guffin-api.jar ETL son ( Nb: mac-guffin-api.jar y sqljdbc42-4.2.jar no desempaquetados, son jar en el contenedor ETL):

mac-guffin-etl.jar | +- org.springframework.boot.loader... | +- BOOT-INF | +- classes | | | +- com.initech.etl.Main.class | | | +- com.initech.etl.Configuration.class | +- lib | +- mac-guffin-api.jar | | | +- com.initech.api.Configuration.class | +- sqljdbc42-4.2.jar | +- com.microsoft.sqlserver.jdbc.SQLServerDriver.class

Aparentemente, la clase de configuración de la clase ETL puede ver el contenido de los archivos JAR incluidos (o al menos los contenidos del contenedor API), pero el contenedor API no parece poder ver el com.microsoft.sqlserver.jdbc.SQLServerDriver.class en el jar servidor JDBC de SQL Server.

Incluso puedo hacer un Class.forName( "com.microsoft.sqlserver.jdbc.SQLServerDriver.class" ) antes de la instanciación del contexto Spring y no tiene ningún problema.

¿Es esto una limitación del cargador de clases? ¿Esto es porque el proyecto API no es Spring Boot? ¿Es por un parámetro de configuración faltante? ¿Que esta pasando aqui?


En algún lugar de su configuración, ha terminado con el nombre de clase que se está utilizando como valor:

''com.microsoft.sqlserver.jdbc.SQLServerDriver''

con comillas simples a su alrededor. Normalmente, el nombre de la clase que se está cargando se imprime sin comillas, doble o simple.

Esto explicaría por qué puede cargar la clase, pero el jar API no. Verifique los archivos de configuración / compilación donde se establece el nombre del controlador.

MANIFESTACIÓN

La única forma en que puedo recibir un mensaje como el tuyo:

Caused by: java.lang.ClassNotFoundException: ''com.microsoft.sqlserver.jdbc.SQLServerDriver''

y no:

Caused by: java.lang.ClassNotFoundException: com.microsoft.sqlserver.jdbc.SQLServerDriver

Es pedir deliberadamente cargar una clase con comillas simples en el nombre. Por ejemplo:

import java.lang.*; public class myclass { public static void test(String thename) { System.out.println("trying " + thename); try { myclass test = (myclass) myclass.class .getClassLoader() .loadClass(thename) .newInstance(); System.out.println(test.toString()); } catch (Exception e){ System.out.println("failed to load " + thename); e.printStackTrace(); } } public static void main(String[] args) { test("my.package.itwontexist"); test("''my.package.itwontexist''"); } }

productos:

trying my.package.itwontexist failed to load my.package.itwontexist java.lang.ClassNotFoundException: my.package.itwontexist at java.net.URLClassLoader.findClass(URLClassLoader.java:381) at java.lang.ClassLoader.loadClass(ClassLoader.java:424) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331) at java.lang.ClassLoader.loadClass(ClassLoader.java:357) at myclass.test(myclass.java:10) at myclass.main(myclass.java:20) trying ''my.package.itwontexist'' failed to load ''my.package.itwontexist'' java.lang.ClassNotFoundException: ''my.package.itwontexist'' at java.net.URLClassLoader.findClass(URLClassLoader.java:381) at java.lang.ClassLoader.loadClass(ClassLoader.java:424) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331) at java.lang.ClassLoader.loadClass(ClassLoader.java:357) at myclass.test(myclass.java:10) at myclass.main(myclass.java:21)


Es posible que obtenga el valor del controlador de la configuración, por ejemplo

my.driver = ''com.microsoft.sqlserver.jdbc.SQLServerDriver''

Y esa configuración devuelve valor con comillas simples. Por favor revisa tus archivos de configuración.


Parece que le falta el archivo MANFIEST.MF que dirige Spring sobre cómo cargar los archivos jar anidados. Aquí hay una jerarquía de ejemplo de la documentación de Spring. Puede leer sobre cómo configurarlo yendo aquí .

MANIFEST.MF debería contener esto (para la estructura siguiente):

Main-Class: org.springframework.boot.loader.JarLauncher Start-Class: com.mycompany.project.MyApplication

Start-Class es su punto de entrada a su aplicación Main-Class es el Loader que necesita para cargar los contenedores anidados.

Estructura de ejemplo:

example.war | +-META-INF | +-MANIFEST.MF +-org | +-springframework | +-boot | +-loader | +-<spring boot loader classes> +-WEB-INF +-classes | +-com | +-mycompany | +-project | +-YourClasses.class +-lib | +-dependency1.jar | +-dependency2.jar +-lib-provided +-servlet-api.jar +-dependency3.jar