Spring Boot ClassLoader

     在使用javaagent+javassist作agent监控Spring Boot项目时,在IDE中正常运行,但是打包成jar之后则发生nosuchclassfound错误,下面对此产生的原因做一些解析。

1.  JVM类加载

JVM类采用的是双亲委托类加载机制。类的加载过程:

  1.   当前类加载器已经加载,则返回类,否则委托父加载器加载此类;
  2.   父加载器执行1的步骤知道Bootstrap ClassLoader
  3.   如果Bootstrap ClassLoader未加载,则由最开始的类加载器加载类。

Spring Boot ClassLoader_第1张图片

其中Bootstrap ClassLoader所加载的是$JAVA_HOME/jre/lib下的jar包和class,ExtClassLoader加载$JAVA_HOME/jre/lib/ext中的jar包,AppClassLoader是加载java.class.path下的类和jar包。

父类加载器所加载的类可以被子类加载器所加载的类使用,但是子类加载器加载的类不能被父类加载器加载的类访问。

2. IDE(Idea)

Spring Boot项目在IDE(idea)中运行,石通过命令java -classpath ... MainClass 启动运行,其中涉及到的类加载器是Bootstrap ClassLoader,ExtClassLoader,AppClassLoader。所有依赖的第三方类库和项目中的class全都由AppClassLoader加载。

3. Jar包

Spring Boot 打包作为Jar时,使用java -jar启动。spring boot的jar的结构


BOOT-INF中包含项目的classes和所有的第三方库(lib);META-INF包含maven相关的文件和MANIFEST.MF;org文件夹为spring boot loader。

Spring Boot ClassLoader_第2张图片

通过jar的MANIFEST.MF文件可以知道,jar的main-class为JarLauncher,start-class为项目中包含main方法的class文件。

JarLauncher在启动时使用LaunchedClassLoader加载jar中的类和第三方库。


LaunchedClassLoader的urls为。

综上所述,springboot的类加载过程应该是AppClassLoader加载org目录下的所有类文件,再由JarLauncher创建LaunchedClassLoader(父类加载器是AppClassLoader)作为默认的类加载器去加载BOOT-INF/classes/和BOOT-INF/lib/中的类和第三方库,并运行start-class中的main方法启动spring boot应用。

4. 发生NoSuchClassFound的原因

-javaagent的jar是通过AppClassLoader加载,在IDE中项目的class文件和第三方库也是通过AppClassLoader加载,所以,IDE中可以正常运行。但是,jar中的类文件和第三方库是通过spring boot的LaunchedClassLoader加载,LaunchedClassLoader的父类加载器AppClassLoader。如果javassist的jar中类有引用由LaunchedClassLoader加载的类,就会报NoSuchClassFound(父类加载器加载的类不能引用子类加载器加载的类)。


javassist可以toClass显性加载来解决


你可能感兴趣的:(Spring Boot ClassLoader)