SprintBoot devtools导致同一个类出现两个不同的Class类(不建议使用SpringBoot Devtools)

目录

一、前言

二、SprintBoot DevTools带来的问题

三、SpringBoot DevTools导致同名Class类出现的原因分析

1、SpringBoot DevTools的原理

2、为什么热启动能够更快

3、合适需要热启动

4、热启动的过程

四、导致同名Class类出现的场景与原因

四、如何解决同名类问题

五、什么场景下使用(为什么不建议使用)

六、惯例


一、前言

最近一段时间,在开发过程中使用了SpringBoot的DevTools做热启动,即减少启动时间。但在使用的过程中却发现使用DevTools会带来一些意想不到的问题。因此我最后果断把SpringBoot DevTools热启动关闭了。我最终的结论是,如果大家对启动时间并没有那么在意,或者当前项目的开发启动时间还可以接受的情况下,还是不要用DevTools了。

接下来我将从DevTools的原理和使用方面一一给大家分析下DevTools到底会带来什么问题。以及这些问题我们可以如何解决,在使用的时候需要注意什么问题。

二、SprintBoot DevTools带来的问题

项目中如何使用SpringBoot DevTools本文就不做介绍了,如果还有不知道的同学可以自行百度。这类教程网上特别多。

我们在使用SpringBoot DevTools的时候,如果应用的第三方包提供了SPI功能(框架的扩展功能),且我们使用了其SPI功能,就会出现代码功能不能正常运行的问题。表象就是代码逻辑没有按照预想的方式执行,如果大家深入分析会发现SPI的实现类出现了两个同名的Class类(包路径完全相同)。对你没有看错,是两个同名的Class类(大家可以思考下为什么同一个JVM中会出现两个同名的Class类)。

那么为什么会出现这样的情况呢?接下来我们将进行深入的分析。

三、SpringBoot DevTools导致同名Class类出现的原因分析

1、SpringBoot DevTools的原理

在分析同名Class类出现的原因之前,我们需要先了解下SpringBoot DevTools的实现原理。

其实SpringBoot DevTools的原理也比较简单,就是通过自定义类加载器实现了类的分离加载。SpringBoot DevTools在启动加载类的时候,它会将我们所有应用的Jar包中类使用默认的类加载器加载(一般为AppClassLoader),然后所有我们自己写的类都是用用其自定义的类加载加载(RestartClassLoader,是AppClassLoader的儿子类加载)。其具体加载过程的核心代码如下。其中findClass负责加载我们编写的类和一些指定的Jar,其他的都会通过异常的方式让父类加载器加载,即其他jar包中的类都会由AppClassLoader类加载器加载。

注意:这里的RestartClassLoader违背了双亲委派原则,它在加载类的时候并没有其看父类加载是否已经存在这个类,而是直接执行加载。

2、为什么热启动能够更快

通过上面我们知道SpringBoot DevTools通过两个不同的类加载器来分别加载我们自己实现的类和Jar包中的类。为了让启动更加快,热启动的时候,其不会重启容器(比如Tomcat),同时只重新加载自定义类,而不加载Jar包中的类。因此热启动的过程相对传统的重启过程少了两个过程:容器的重启、Jar包中的类加载。所以它比普通的重启过程更快。

3、何时需要热启动

有了两个类加载器加载后,SpringBoot DevTools是如何触发热启动的呢?SpringBoot DevTools主要通过两种方式来触发热启动:不断扫描所有的类和不断扫描指定的Trigger文件。

如果我们通过trigger-file制定了Trigger文件,那么SpringBoot DevTools后端就会不断扫描这文件,如果发现其变化了(通过修改时间或者文件大小),然后就会触发启动过程。

如果没有指定trigger-file文件,SpringBoot DevTools则会扫描所有的Class编译文件,当发现有文件变化时,就触发热启动过程。

如下是不断扫描文件的代码:

如下是如何判断文件是否发送变化的代码:

4、热启动的过程

当监控到文件变化之后,就会触发热启动。该过程通过Restarter类来完成。此过程只会重新加载RestartClassLoader类加载器负责的类,其大概过程如下:

  • 销毁之前的类加载器RestartClassLoader的实例(包括一些清理工作)
  • 新建一个新的RestartClassLoader类实例
  • 使用新的RestartClassLoader类实例来加载我们自己的类和指定jar中的类。

整个热启动的大致过程如下:

四、导致同名Class类出现的场景与原因

前提:我们引入了一个支持SPI扩展功能的框架jar包,同时使用了其SPI功能,即实现了自己的扩展能力,假设该实现类为SpiImpl。当AppClassLoader在加载jar包中的类的时候,其会自动加载SpiImpl类(此时的类加载器为AppClassLoader)。然后当使用RestartClassLoader类加载器加载咱们自己编写的代码时,如果SpiImpl类被业务代码使用到了,又会再一次加载SpiImpl类(此时的类加载器为RestartClassLoader)。那么这个时候我们的JVM中就存在了两个同名的SpiImpl的Class类。因此业务执行是就会带来一些问题。

这里也就回答了上面的问题:什么场景下在JVM中同一个类会出现两个相同的Class类。即在同一个类被不同的类加载器加载的时候就会出现。

四、如何解决同名类问题

SpringBoot这么牛逼的项目当然是考虑到了这个问题,它提供了一种方法来解决这个问题。我们可以先试想一下,如果要我们需要自己来解决这个问题应该如何处理呢?很明显,最简单的办法就是让提供SPI能力的jar包中的类和我们的代码在同一个类加载器中加载。这样SpiImpl就不会同时存在两个不同的类加载器中了。

当然我们的SpringBoot也是使用的这个方法,他提供了restart.include来指定哪些jar包需要用RestartClassLoader类加载器加载,即和我们的代码使用同一个类加载器加载。其官方说明如下:

所以我们可以通过restart.include来制定我们提供SPI功能的jar包都使用RestartClassLoader类来加载,这样即可解决同名Class类的问题。

五、什么场景下使用(为什么不建议使用)

其实我最开始也是通过了restart.include来解决同名类的问题。但是随着开发的过程中(过了一段时间),程序由于引入了新的SPI功能又莫名的出现程序功能异常,此时由于时间过去了很久,俺早忘记了SpringBoot DevTools这回事。于是定位了很久,后来突然恍然大悟,原来是DevTools这货带来的锅。于是俺一气之下直接把DevTools关闭了。

其实当你和楼主遇到的情况一下,如果你很久突然忘记了,然后突然出问题往往需要定位很久才能找到原因。索性还不如直接关闭这个潜在隐患。特别是当你的项目出去构架构建阶段,很有可能不时需要引入很多jar包,可能需要用到SPI的场景。

当然,如果你的工程很成熟,且热启动确实能够带来比较明显的效率提升,当然还是建议使用SpringBoot DevTools。但是你一定要记住这个潜在的隐患,免得有一天像楼主一样,需要耗费到了的时间在这个隐患上。

六、惯例

如果你对本文有任何疑问或者高见,欢迎添加公众号共同交流探讨(添加公众号可以获得”Java高级架构“上10G的视频和图文资料哦)。

  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值