Why Spring Boot Devtools Triggers ClassCastException and How to Fix It

This article explains why adding spring-boot-devtools can trigger a ClassCastException due to its dual classloader mechanism, demonstrates how to reproduce the issue in a Maven multi‑module project, and provides two practical solutions: customizing the RestartClassLoader or removing the devtools dependency.

Ziru Technology
Ziru Technology
Ziru Technology
Why Spring Boot Devtools Triggers ClassCastException and How to Fix It

1. Why use spring-boot-devtools?

Spring Boot provides an additional set of tools to improve the development experience. The spring-boot-devtools module can be added to any project to enable development‑time features such as automatic restarts.

Typical Maven inclusion:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <optional>true</optional>
</dependency>

2. Then the login service crashed...

Our project uses Dubbo, separating services and calls. A DarkPortalHeader class is shared via a common module and accessed by both consumer and provider. The provider extends Dubbo's Filter to retrieve header data.

public class DarkPortalFilter implements Filter {
    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        String dpHeaderJson = invocation.getAttachment(DP_HEADER_KEY);
        if (!StringUtils.isBlank(dpHeaderJson)) {
            DarkPortalHeader darkPortalHeader = JSON.parseObject(dpHeaderJson, DarkPortalHeader.class);
            DarkPortalHeaderHolder.set(darkPortalHeader);
        }
        try {
            return invoker.invoke(invocation);
        } finally {
            DarkPortalHeaderHolder.remove();
        }
    }
}

One day a java.lang.ClassCastException occurred:

java.lang.ClassCastException: com.ziroom.xxx.DarkPortalHeader cannot be cast to com.ziroom.xxx.DarkPortalHeader

The cause is that the same class was loaded by two different classloaders: the standard AppClassLoader and Spring Boot Devtools' RestartClassLoader. The restart technology uses a base classloader for unchanged classes (e.g., third‑party JARs) and a restart classloader for classes under active development. When the application restarts, the restart classloader is discarded and recreated, leading to the mismatch.

Thus, JSON.class from a third‑party JAR is loaded by AppClassLoader, while DarkPortalHeader from the project is loaded by RestartClassLoader, causing the ClassCastException.

3. Reproducing the problem

Create a Maven multi‑module project with the following structure:

└── common
    └── src/main/java/com/ziroom/model/City.java
    └── pom.xml
└── demo
    └── src/main/java/com/ziroom/Application.java
    └── pom.xml
pom.xml (parent)

Parent pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.ziroom</groupId>
    <artifactId>sample</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>common</module>
        <module>demo</module>
    </modules>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

common/pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>sample</artifactId>
        <groupId>com.ziroom</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>common</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.4</version>
        </dependency>
    </dependencies>
</project>

demo/pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>sample</artifactId>
        <groupId>com.ziroom</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>demo</artifactId>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.1.7.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <version>2.1.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.1.41</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.8</version>
        </dependency>
        <dependency>
            <groupId>com.gracier</groupId>
            <artifactId>basic</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>

City.java

package com.ziroom.model;

import lombok.Data;

@Data
public class City {
    private String name;
    public City() {}
    public City(String name) { this.name = name; }
}

Application.java

package com.ziroom;

import com.alibaba.fastjson.JSON;
import com.ziroom.model.City;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.event.EventListener;

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
    @EventListener
    public void onApplicationStarted(ApplicationStartedEvent event) {
        City city = new City("北京");
        // should throw java.lang.ClassCastException
        String json = JSON.toJSONString(city);
    }
}

Running the application prints the following exception:

java.lang.ClassCastException: com.ziroom.model.City cannot be cast to com.ziroom.model.City

How to solve?

Solution 1: Customize the RestartClassLoader by creating a META-INF/spring-devtools.properties file with the following content:

restart.include.fastjson=/fastjson-[\\w\\d-\.]+\.jar

Save the file and rerun the program; the exception disappears.

Solution 2: Remove the spring-boot-devtools dependency from pom.xml.

5. Summary

For a long time there has been no perfect hot‑reload solution for backend development. Spring Boot Devtools offers benefits such as faster restarts, but it also introduces classloader‑related issues that are hard to predict. Its use should be limited to the development phase, and developers should understand its internals before relying on it.

6. References

https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-devtools.html

https://github.com/spring-projects/spring-boot/issues/3316

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaDubbomavenSpring BootclassloaderdevtoolsClassCastException
Ziru Technology
Written by

Ziru Technology

Ziru Official Tech Account

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.