今天发生一个诡异的问题,就是当代码打包进jar包后,调用时发现类找不到。
下面是一段定时器Bean自动创建的启动代码,采用SpringContext拿到接口Bean列表后,进行quartz启动。由于quartz的JobBuilder.newJob需要Class,因此很简单的想到去Class.forName,下面代码在IDE开发环境也是没问题的:
Class<? extends Job> aClass = (Class <? extends Job>)Class.forName(jobClass);
但是打包jar发布后在发布环境却出现了ClassNotFoundException。
问了下GPT,GPT如下回答:
在Spring Boot应用程序中,使用Class.forName()
加载类与直接引入组件(Jar包)的方式得到的Class
对象可能不同,这通常是由于类加载器的差异引起的。
在Java中,类加载器负责加载类到内存中。Class.forName()
方法默认使用的是调用它的类的类加载器。而在Spring Boot中,类加载器的层次结构可能会有所不同,具体取决于应用程序的配置和类路径的设置。
当你直接引入一个组件(Jar包)时,通常会使用默认的类加载器(如应用程序类加载器)来加载该组件中的类。这意味着直接引入的类与通过Class.forName()
加载的类可能由不同的类加载器加载,即使它们具有相同的类名和包名,它们也被视为不同的类。
为了解决这个问题,你可以通过提供明确的类加载器来调用Class.forName()
方法,例如:
Class.forName("com.example.MyClass", true, Thread.currentThread().getContextClassLoader());
这将确保Class.forName()
使用与应用程序中其他组件相同的类加载器加载类,从而避免类加载器的不一致性问题。
于是问题解决,看来spring初始化加载器和主加载器不同。修改为以下代码于是解决:
Class<? extends Job> aClass = (Class <? extends Job>)Class.forName(jobClass, true, Thread.currentThread().getContextClassLoader());