解決SpringBoot使用devtools導(dǎo)致的類型轉(zhuǎn)換異常問(wèn)題
問(wèn)題:
最近在使用新框架SpringBoot + shiro + spring-data-jpa時(shí),為了體驗(yàn)下spring自帶的熱部署工具的便捷,于是引入了
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <!-- optional=true,依賴不會(huì)傳遞,該項(xiàng)目依賴devtools;之后依賴myboot項(xiàng)目的項(xiàng)目如果想要使用devtools,需要重新引入 --> <optional>true</optional> </dependency>
在起初并沒(méi)遇到什么問(wèn)題,當(dāng)使用shiro的session管理,而且用的sessionDao是redis實(shí)現(xiàn)的,然后再使用Session存取屬性時(shí),發(fā)現(xiàn)存進(jìn)去的屬性,再取出來(lái)后,就會(huì)出現(xiàn)類型轉(zhuǎn)換異常ClassCastException
分析:
然后自己寫了一大推單元測(cè)試模擬就是沒(méi)問(wèn)題,后來(lái)突然意識(shí)到會(huì)不會(huì)是因?yàn)镃lassLoader不同導(dǎo)致的類型轉(zhuǎn)換異常呢,然后注意了下項(xiàng)目啟動(dòng)時(shí)加載項(xiàng)目中的類使用的加載器都是
org.springframework.boot.devtools.restart.classloader.RestartClassLoader
而從shiro session 取出來(lái)的對(duì)象(從redis中取出經(jīng)過(guò)反序列化)的類加載器都是
sun.misc.Launcher.AppClassLoader
很明顯會(huì)導(dǎo)致類型轉(zhuǎn)換異常,原來(lái)Spring的dev-tools為了實(shí)現(xiàn)重新裝載class自己實(shí)現(xiàn)了一個(gè)類加載器,來(lái)加載項(xiàng)目中會(huì)改變的類,方便重啟時(shí)將新改動(dòng)的內(nèi)容更新進(jìn)來(lái),其實(shí)其中官方文檔中是有做說(shuō)明的:
By default, any open project in your IDE will be loaded using the “restart” classloader, and any regular .jar file will be loaded using the “base” classloader. If you work on a multi-module project, and not each module is imported into your IDE, you may need to customize things. To do this you can create a META-INF/spring-devtools.properties file. The spring-devtools.properties file can contain restart.exclude. and restart.include. prefixed properties. The include elements are items that should be pulled up into the “restart” classloader, and the exclude elements are items that should be pushed down into the “base” classloader. The value of the property is a regex pattern that will be applied to the classpath.
解決:
方案一、解決方案就是在resources目錄下面創(chuàng)建META-INF文件夾,然后創(chuàng)建spring-devtools.properties文件,文件加上類似下面的配置:
restart.exclude.companycommonlibs=/mycorp-common-[\w-]+.jar restart.include.projectcommon=/mycorp-myproj-[\w-]+.jar
All property keys must be unique. As long as a property starts with restart.include. or restart.exclude. it will be considered. All META-INF/spring-devtools.properties from the classpath will be loaded. You can package files inside your project, or in the libraries that the project consumes.
方案二、不使用spring-boot-devtools
針對(duì)方案一作一個(gè)詳細(xì)的案例進(jìn)行分析說(shuō)明,以及解決問(wèn)題
首先準(zhǔn)備一個(gè)jar包,里面包含序列化以及反序列化的功能。
并打包,在springboot項(xiàng)目中引入
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <!-- 這個(gè)包是我自己創(chuàng)建的序列化以及反序列化工具包 --> <dependency> <groupId>com.example</groupId> <artifactId>devtools-serialization</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
簡(jiǎn)單的配置下springboot項(xiàng)目,并模擬使用jar中的序列化工具類進(jìn)行處理對(duì)象如下
@SpringBootApplication public class PortalApplication { public static void main(String[] args) throws Exception { ConfigurableApplicationContext context = SpringApplication.run(PortalApplication.class, args); DemoBean demoBean = new DemoBean(); SerializationUtils.serialize(demoBean); Object deserialize = SerializationUtils.deserialize(); System.out.println(PortalApplication.class.getClassLoader()); //這里對(duì)象引用是Object類型 System.out.println(deserialize); System.out.println(deserialize.getClass().getClassLoader()); context.getBeanFactory().destroySingletons(); } }
如上,是不會(huì)報(bào)錯(cuò)的,因?yàn)镺bject是bootstrap引導(dǎo)類加載器加載的,因此不會(huì)產(chǎn)生任何問(wèn)題,
但是如果改成下面這樣
//... public static void main(String[] args) throws Exception { ConfigurableApplicationContext context = SpringApplication.run(PortalApplication.class, args); DemoBean demoBean = new DemoBean(); SerializationUtils.serialize(demoBean); Object deserialize = SerializationUtils.deserialize(); System.out.println(PortalApplication.class.getClassLoader()); //注意這里進(jìn)行了一次類型強(qiáng)轉(zhuǎn) System.out.println((DemoBean)deserialize); System.out.println(deserialize.getClass().getClassLoader()); context.getBeanFactory().destroySingletons(); } //...
結(jié)果是會(huì)拋出:
Exception in thread "restartedMain" java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) Caused by: java.lang.ClassCastException: com.sample.serial.DemoBean cannot be cast to com.sample.serial.DemoBean at com.sample.PortalApplication.main(PortalApplication.java:27) ... 5 more
而觀察上面輸出的ClassLoader信息會(huì)發(fā)現(xiàn)分別為
org.springframework.boot.devtools.restart.classloader.RestartClassLoader@63059d5a sun.misc.Launcher$AppClassLoader@18b4aac2
這就是為什么會(huì)明明沒(méi)問(wèn)題,卻仍然拋了個(gè)ClassCastException的根源所在。
那么如何解決這個(gè)問(wèn)題呢?
將輸出的ClassLoader信息保持一致即可,要么都是RestartClassLoader要么都是
AppClassLoader
這里參考spring官方文檔給出的配置方法進(jìn)行處理。
在resources下創(chuàng)建META-INF/spring-devtools.properties
如圖:
下一步在spring-devtools.properties添加配置
restart.include.projectcommon=/devtools-serialization-[\\w.-]+.jar
注意這里我需要包含的jar包名稱為devtools-serialization-1.0-SNAPSHOT.jar
配置的key以restart.include.開(kāi)頭即可
restart.include.*
value 為一個(gè)正則表達(dá)式
下面再次運(yùn)行程序查看效果:
沒(méi)有異常產(chǎn)生
控制臺(tái)輸出classLoader信息為
org.springframework.boot.devtools.restart.classloader.RestartClassLoader@1d9fbdd4 DemoBean{age=null, name='null'} org.springframework.boot.devtools.restart.classloader.RestartClassLoader@1d9fbdd4
問(wèn)題完美解決。
補(bǔ)充知識(shí):Springboot+devtools配置熱部署
Spring Boot提供了spring-boot-devtools這個(gè)模塊來(lái)使應(yīng)用支持熱部署,可以提高開(kāi)發(fā)者的開(kāi)發(fā)效率,無(wú)需手動(dòng)重啟Spring Boot應(yīng)用就能實(shí)現(xiàn)自動(dòng)加載,之前寫了一篇可以自動(dòng)加載springboot靜態(tài)文件的,這次的只需要在原來(lái)的基礎(chǔ)上再加一些配置即可實(shí)現(xiàn)springboot工程的熱部署,步驟如下:
1、pom文件增加依賴:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <fork>true</fork> <!--重要--> </configuration> </plugin> </plugins> </build>
2、yml文件中添加配置使其生效:
# devtools debug: true spring: devtools: restart: enabled: true #設(shè)置開(kāi)啟熱部署 freemarker: cache: false #頁(yè)面不加載緩存,修改即時(shí)生效
3、快捷鍵:Ctrl+Alt+S
4、快捷鍵:Ctrl+Shift+A,輸入Registry,點(diǎn)擊進(jìn)入勾選:
以上這篇解決SpringBoot使用devtools導(dǎo)致的類型轉(zhuǎn)換異常問(wèn)題就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java編程中的構(gòu)造函數(shù)詳細(xì)介紹
這篇文章主要介紹了Java編程中的構(gòu)造函數(shù)詳細(xì)介紹,介紹了其概念,格式,與其他函數(shù)的區(qū)別,作用等相關(guān)內(nèi)容,具有一定參考價(jià)值,需要的朋友可以了解下。2017-11-11Java調(diào)用Oracle存儲(chǔ)過(guò)程詳解
這篇文章主要介紹了Java調(diào)用Oracle存儲(chǔ)過(guò)程詳解的相關(guān)資料,需要的朋友可以參考下2017-02-02java開(kāi)源項(xiàng)目jeecgboot的超詳細(xì)解析
JeecgBoot是一款基于BPM的低代碼平臺(tái),下面這篇文章主要給大家介紹了關(guān)于java開(kāi)源項(xiàng)目jeecgboot的相關(guān)資料,文中通過(guò)圖文以及實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-10-10Java實(shí)現(xiàn)二叉搜索樹(shù)的插入、刪除功能
這篇文章主要介紹了Java實(shí)現(xiàn)二叉搜索樹(shù)的插入、刪除,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-01-01HashMap和List遍歷方法及如何遍歷刪除元素總結(jié)
在本篇文章中小編給大家分享了關(guān)于HashMap和List遍歷方法及如何遍歷刪除元素知識(shí)點(diǎn)總結(jié),需要的朋友們參考下。2019-05-05J2EE Servlet上傳文件到服務(wù)器并相應(yīng)顯示功能的實(shí)現(xiàn)代碼
這篇文章主要介紹了J2EE Servlet上傳文件到服務(wù)器,并相應(yīng)顯示,在文中上傳方式使用的是post不能使用get,具體實(shí)例代碼大家參考下本文2018-07-07使用maven運(yùn)行Java Main的三種方法解析
這篇文章主要介紹了使用maven運(yùn)行Java Main的三種方式的相關(guān)內(nèi)容,具有一定參考價(jià)值,需要的朋友可以了解下。2017-10-10Java Collection集合遍歷運(yùn)行代碼實(shí)例
這篇文章主要介紹了Java Collection集合遍歷運(yùn)行代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04