spring-bootspring-boot-devtools

Spring Boot devtools - sometimes reloaded bean is not picked up on restart


I have some troubles with spring-devtools restart feature. It seems that sometimes for unknown reasons changed and reloaded bean is not picked up.

Application class:

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

Command line runner:

@Component
@RequiredArgsConstructor
@Slf4j
public class TestCommandLineRunner implements CommandLineRunner {
    private final TestService testService;

    @Override
    public void run(String... args) throws Exception {
        log.info("{}", testService.test());
    }
}

Service that is changed and reloaded:

@Service
public class TestService {
    public int test() {
        return 12; //I am changing this number for tests
    }
}

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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.8.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.chrosciu</groupId>
    <artifactId>devtools-test</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>devtools-test</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>11</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

Usually first reload works fine:

2019-09-15 12:08:31.788  INFO 16464 --- [       Thread-5] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.1.8.RELEASE)

2019-09-15 12:08:32.121  INFO 16464 --- [  restartedMain] com.chrosciu.DemoApplication             : Starting DemoApplication on chma1 with PID 16464 (C:\Users\chma\gitroot\devtools-test\target\classes started by chma in C:\Users\chma\gitroot\devtools-test)
2019-09-15 12:08:32.122  INFO 16464 --- [  restartedMain] com.chrosciu.DemoApplication             : No active profile set, falling back to default profiles: default
2019-09-15 12:08:32.663  INFO 16464 --- [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2019-09-15 12:08:32.664  INFO 16464 --- [  restartedMain] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2019-09-15 12:08:32.666  INFO 16464 --- [  restartedMain] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.24]
2019-09-15 12:08:32.725  INFO 16464 --- [  restartedMain] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2019-09-15 12:08:32.725  INFO 16464 --- [  restartedMain] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 600 ms
2019-09-15 12:08:32.833  INFO 16464 --- [  restartedMain] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2019-09-15 12:08:32.900  INFO 16464 --- [  restartedMain] o.s.b.d.a.OptionalLiveReloadServer       : LiveReload server is running on port 35729
2019-09-15 12:08:32.924  INFO 16464 --- [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2019-09-15 12:08:32.925  INFO 16464 --- [  restartedMain] com.chrosciu.DemoApplication             : Started DemoApplication in 0.86 seconds (JVM running for 18.403)
2019-09-15 12:08:32.926  INFO 16464 --- [  restartedMain] com.chrosciu.TestCommandLineRunner       : 8
2019-09-15 12:08:32.927  INFO 16464 --- [  restartedMain] .ConditionEvaluationDeltaLoggingListener : Condition evaluation unchanged

But the second or third crashes as follows with info about missing bean:

2019-09-15 12:09:15.482  INFO 16464 --- [      Thread-21] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.1.8.RELEASE)

2019-09-15 12:09:15.842  INFO 16464 --- [  restartedMain] com.chrosciu.DemoApplication             : Starting DemoApplication on chma1 with PID 16464 (C:\Users\chma\gitroot\devtools-test\target\classes started by chma in C:\Users\chma\gitroot\devtools-test)
2019-09-15 12:09:15.843  INFO 16464 --- [  restartedMain] com.chrosciu.DemoApplication             : No active profile set, falling back to default profiles: default
2019-09-15 12:09:16.396  INFO 16464 --- [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2019-09-15 12:09:16.397  INFO 16464 --- [  restartedMain] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2019-09-15 12:09:16.398  INFO 16464 --- [  restartedMain] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.24]
2019-09-15 12:09:16.483  INFO 16464 --- [  restartedMain] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2019-09-15 12:09:16.483  INFO 16464 --- [  restartedMain] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 637 ms
2019-09-15 12:09:16.714  WARN 16464 --- [  restartedMain] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'testCommandLineRunner' defined in file [C:\Users\chma\gitroot\devtools-test\target\classes\com\chrosciu\TestCommandLineRunner.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.chrosciu.TestService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
2019-09-15 12:09:16.714  INFO 16464 --- [  restartedMain] o.apache.catalina.core.StandardService   : Stopping service [Tomcat]
2019-09-15 12:09:16.723  INFO 16464 --- [  restartedMain] ConditionEvaluationReportLoggingListener : 

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2019-09-15 12:09:16.969 ERROR 16464 --- [  restartedMain] o.s.b.d.LoggingFailureAnalysisReporter   : 

***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 0 of constructor in com.chrosciu.TestCommandLineRunner required a bean of type 'com.chrosciu.TestService' that could not be found.


Action:

Consider defining a bean of type 'com.chrosciu.TestService' in your configuration.

Does anyone have a clue what is going on ? Any help appreciated.


Solution

  • It may be that whatever you are using to compile the changes you have made is deleting the class file and then recompiling the source rather than writing the new class file over the top of the old one. If the time between the class file being deleted and the source being recompiled is greater than DevTools' quiet period, the app will be restarted without the changed class – DevTools will believe that you have deleted it rather than changing it.

    You may want to increase the quiet period so that DevTools sees both the deletion and the creation of the recompiled class file in the same batch. The default quiet period is 400ms. The following example will set it to 1 second:

    spring.devtools.restart.quiet-period=1s
    

    You can learn more about DevTools' quiet period and how to configure it in the relevant section of the documentation.