为何Spring Boot DevTools会导致main方法执行两次?
嘿,这个问题我之前帮好几个开发者解决过,其实是Spring Boot DevTools的Restart功能在搞鬼,我给你理清楚原因和解决办法:
DevTools的核心功能之一是快速重启,为了实现这个功能,它会使用双类加载器机制:
- 一个
BaseClassLoader用来加载第三方依赖库(这些库很少变动) - 一个
RestartClassLoader用来加载你的应用代码(方便修改后快速重启)
当你启动应用时,JVM会先通过原始类加载器执行一次main方法,之后DevTools会用RestartClassLoader重新加载应用类并再次执行main方法,这就导致你的System.out.println("test")被执行了两次——不管你把它放在main方法内部,还是类的静态初始化块(也就是你说的main方法外部)里,都会因为类被加载两次而触发两次输出。
根据你的需求,有几个不同的方案可选:
1. 直接禁用Restart功能
如果你不需要DevTools的热重启功能,或者可以接受用其他方式替代,直接在配置文件里关闭它:
在application.properties中添加:
spring.devtools.restart.enabled=false
或者在application.yml中:
spring: devtools: restart: enabled: false
这样DevTools就不会创建额外的类加载器,你的代码只会被执行一次。
2. 保留DevTools功能,调整代码执行时机
如果你还想保留热部署等功能,可以把需要执行的代码放到Spring应用上下文初始化完成后的回调里,比如实现ApplicationRunner接口:
import org.springframework.boot.ApplicationRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; @SpringBootApplication public class YourApplication { public static void main(String[] args) { SpringApplication.run(YourApplication.class, args); } @Bean public ApplicationRunner startupRunner() { return args -> { System.out.println("test"); }; } }
这个回调只会在Spring容器完全启动后执行一次,不会被DevTools的类加载机制影响。
3. 检查IDE启动配置(可选)
偶尔也会出现IDE误触发两次启动的情况,比如同时运行了main类和Spring Boot插件的启动任务。你可以打开IDE的运行配置,确认只有一个启动项在运行。
这其实不是DevTools的bug,而是它为了实现快速重启而设计的特性。双类加载器让DevTools可以在你修改代码后,只重新加载应用类而不用重启整个JVM,大大提升开发效率,但代价就是启动时会有一次额外的类加载和main方法执行。
内容的提问来源于stack exchange,提问作者mrtasln

