google-site-verification: google61fe8ba583a51912.html
Spring Boot DevTools for Hot Reloading in Java

Spring Boot DevTools is a set of development-time tools that dramatically improves the development experience by providing automatic restart, live reload, and property defaults. This article explores how to use DevTools for efficient hot reloading in Java applications.

Setup and Configuration

1. Adding DevTools to Your Project

Maven:

<dependencies> <!-- Other dependencies --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> </dependencies>

Gradle:

dependencies { // Other dependencies developmentOnly 'org.springframework.boot:spring-boot-devtools' }

2. Basic Configuration

application.properties:

# DevTools specific properties spring.devtools.restart.enabled=true spring.devtools.restart.log-condition-evaluation-delta=false spring.devtools.livereload.enabled=true spring.devtools.livereload.port=35729 # Exclude specific paths from restart spring.devtools.restart.exclude=static/**,public/**,templates/** # Additional restart trigger paths spring.devtools.restart.additional-paths=src/main/java

application.yml:

spring: devtools: restart: enabled: true log-condition-evaluation-delta: false exclude: "static/**,public/**,templates/**" additional-paths: "src/main/java" livereload: enabled: true port: 35729

Automatic Restart Functionality

3. How Automatic Restart Works

Spring Boot DevTools monitors classpath changes and automatically restarts the application. The restart uses two classloaders:

  • Base ClassLoader: For third-party jars (don't change frequently)
  • Restart ClassLoader: For your application code (changes frequently)
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }

4. Customizing Restart Behavior

Custom Restart Configuration:

@Component public class CustomRestartListener implements RestartListener { @Override public void beforeRestart() { System.out.println("Application is about to restart..."); // Clean up resources or save state } @Override public void afterRestart() { System.out.println("Application has restarted successfully!"); // Reinitialize components if needed } }

Excluding Specific Files:

@Configuration public class DevToolsConfig { @Bean public DevToolsRestartCustomizer devToolsRestartCustomizer() { return new DevToolsRestartCustomizer() { @Override public void customize(RestartInitializer initializer) { // Exclude configuration files from triggering restart initializer.exclude("config/**"); initializer.exclude("logs/**"); } }; } }

LiveReload Integration

5. Browser Live Reload

DevTools includes a LiveReload server that automatically refreshes the browser when static resources change.

HTML with LiveReload:

<!DOCTYPE html> <html> <head> <title>My Application</title> <!-- LiveReload script for development --> <script> if (window.location.hostname === 'localhost') { document.write('<script src="http://' + (location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1"></' + 'script>') } </script> </head> <body> <h1>Welcome to My Application</h1> <div id="content"> <!-- Dynamic content --> </div> </body> </html>

6. Custom LiveReload Configuration

@Configuration public class LiveReloadConfig { @Autowired private LiveReloadServer liveReloadServer; @Bean public LiveReloadServer liveReloadServer() { return new LiveReloadServer(35729); } @EventListener public void onClassPathChanged(ClassPathChangedEvent event) { // Trigger LiveReload when classpath changes liveReloadServer.triggerReload(); } }

Global Settings

7. Global DevTools Configuration

Create .spring-boot-devtools.properties in your $HOME directory:

~/.spring-boot-devtools.properties:

# Global restart excludes spring.devtools.restart.exclude=log4j2.xml,logback-spring.xml # Global trigger file spring.devtools.restart.trigger-file=.reloadtrigger # Remote debug configuration spring.devtools.remote.debug.enabled=true spring.devtools.remote.debug.local-port=8000

Property Defaults

8. Development-Specific Properties

DevTools automatically sets sensible development-time defaults:

@Configuration public class DevelopmentConfig { @Value("${spring.h2.console.enabled:false}") private boolean h2ConsoleEnabled; @Value("${spring.thymeleaf.cache:false}") private boolean thymeleafCache; @Value("${spring.web.resources.cache.period:0}") private int resourcesCachePeriod; @PostConstruct public void init() { System.out.println("Development configuration loaded:"); System.out.println("H2 Console: " + h2ConsoleEnabled); System.out.println("Thymeleaf Cache: " + thymeleafCache); System.out.println("Resources Cache: " + resourcesCachePeriod); } }

Remote Development

9. Remote Applications with DevTools

Client Application (Development Machine):

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

Remote Application Configuration:

# Enable remote support spring.devtools.remote.secret=mysecret # Remote URL spring.devtools.remote.url=http://my-remote-server:8080

10. Remote Restart Endpoint

@RestController public class DevelopmentController { private final DevToolsRestartHandler restartHandler; public DevelopmentController(DevToolsRestartHandler restartHandler) { this.restartHandler = restartHandler; } @PostMapping("/dev/restart") public ResponseEntity<String> triggerRestart() { restartHandler.restart(); return ResponseEntity.ok("Application restart triggered"); } @GetMapping("/dev/info") public Map<String, Object> getDevInfo() { Map<String, Object> info = new HashMap<>(); info.put("uptime", ManagementFactory.getRuntimeMXBean().getUptime()); info.put("restarts", getRestartCount()); return info; } private long getRestartCount() { // Custom logic to track restarts return 0L; } }

Integration with IDEs

11. IntelliJ IDEA Configuration

Enable automatic build:

  • Go to Settings → Build, Execution, Deployment → Compiler
  • Check Build project automatically

Registry setting:

  • Press Ctrl+Shift+A, type "Registry"
  • Find and enable compiler.automake.allow.when.app.running

12. Eclipse Configuration

Enable automatic build:

  • Project → Build Automatically

.spring-boot-devtools.properties in project:

spring.devtools.restart.poll-interval=1000 spring.devtools.restart.quiet-period=400

Custom Development Tools

13. Creating Custom DevTools

@Component public class DevelopmentWatcher { private static final Logger logger = LoggerFactory.getLogger(DevelopmentWatcher.class); private final ApplicationEventPublisher eventPublisher; private final FileWatcher fileWatcher; public DevelopmentWatcher(ApplicationEventPublisher eventPublisher) { this.eventPublisher = eventPublisher; this.fileWatcher = new FileWatcher(); } @PostConstruct public void startWatching() { fileWatcher.watchDirectory("src/main/resources/templates", this::onTemplateChange); fileWatcher.watchDirectory("src/main/resources/static", this::onStaticResourceChange); } private void onTemplateChange(Path changedFile) { logger.info("Template changed: {}", changedFile); eventPublisher.publishEvent(new TemplateChangeEvent(this, changedFile)); } private void onStaticResourceChange(Path changedFile) { logger.info("Static resource changed: {}", changedFile); eventPublisher.publishEvent(new StaticResourceChangeEvent(this, changedFile)); } } @Component public class DevelopmentEventListener { @EventListener public void handleTemplateChange(TemplateChangeEvent event) { // Clear template cache // Recompile templates if needed System.out.println("Template changed: " + event.getChangedFile()); } @EventListener public void handleStaticResourceChange(StaticResourceChangeEvent event) { // Trigger browser refresh System.out.println("Static resource changed: " + event.getChangedFile()); } }

14. Database Development Tools

@Component public class DatabaseDevelopmentTools { @Autowired private DataSource dataSource; @Value("${spring.datasource.url:}") private String datasourceUrl; @EventListener public void onApplicationReady(ApplicationReadyEvent event) { if (isDevelopmentProfile() && isH2Database()) { initializeDevelopmentData(); } } private boolean isDevelopmentProfile() { return Arrays.stream(environment.getActiveProfiles()) .anyMatch(profile -> profile.equals("dev") || profile.equals("development")); } private boolean isH2Database() { return datasourceUrl.contains("h2"); } private void initializeDevelopmentData() { try (Connection conn = dataSource.getConnection(); Statement stmt = conn.createStatement()) { // Insert development test data stmt.execute("INSERT INTO users (username, email) VALUES ('devuser', '[email protected]')"); logger.info("Development data initialized"); } catch (SQLException e) { logger.warn("Failed to initialize development data", e); } } }

Performance Optimization

15. Optimizing Restart Performance

@Configuration public class PerformanceConfig { @Bean @Profile("dev") public RestartClassLoader restartClassLoader() { return new RestartClassLoader() { @Override protected boolean isExcludedFromRestart(String className) { // Exclude large libraries to improve restart performance return className.startsWith("org.hibernate.") || className.startsWith("com.fasterxml.jackson.") || className.startsWith("org.springframework.security."); } }; } }

application-dev.properties:

# Optimize for faster restarts spring.devtools.restart.poll-interval=1000 spring.devtools.restart.quiet-period=500 # Exclude large libraries spring.devtools.restart.exclude= org.hibernate./*, com.fasterxml.jackson./*, org.springframework.security./*

Testing with DevTools

16. Test Configuration

@SpringBootTest @TestPropertySource(properties = { "spring.devtools.restart.enabled=false", "spring.devtools.livereload.enabled=false" }) public class ApplicationTests { @Test public void contextLoads() { // Test that application starts correctly } @Test @DirtiesContext public void testWithRestart() { // Test that survives application restart } } @DataJpaTest @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) public class RepositoryTests { @Autowired private TestEntityManager entityManager; @Autowired private UserRepository userRepository; @Test public void testRepositoryOperations() { // Repository tests work with DevTools User user = new User("testuser", "[email protected]"); entityManager.persist(user); User found = userRepository.findByUsername("testuser"); assertThat(found).isNotNull(); } }

Troubleshooting Common Issues

17. Common Problems and Solutions

@Component public class DevToolsDiagnostics { private static final Logger logger = LoggerFactory.getLogger(DevToolsDiagnostics.class); @EventListener public void onApplicationEvent(ApplicationEvent event) { if (event instanceof RestartEvent) { logger.info("Restart event detected"); logClasspathInfo(); } } private void logClasspathInfo() { try { ClassLoader classLoader = getClass().getClassLoader(); if (classLoader instanceof URLClassLoader) { URL[] urls = ((URLClassLoader) classLoader).getURLs(); logger.info("Classpath URLs: {}", Arrays.toString(urls)); } } catch (Exception e) { logger.warn("Failed to log classpath info", e); } } }

Common Configuration Fixes:

# If restarts are too slow spring.devtools.restart.quiet-period=500ms spring.devtools.restart.poll-interval=1s # If LiveReload isn't working spring.devtools.livereload.port=35729 spring.devtools.livereload.enabled=true # If specific files aren't triggering restart spring.devtools.restart.additional-paths=src/main/java,src/main/resources spring.devtools.restart.trigger-file=.reloadtrigger

Best Practices

18. Development Workflow Optimization

@Profile("dev") @Component public class DevelopmentAssistant { @Autowired private Environment environment; @PostConstruct public void printDevelopmentInfo() { System.out.println("=== Development Mode Active ==="); System.out.println("Active Profiles: " + Arrays.toString(environment.getActiveProfiles())); System.out.println("DevTools: Enabled"); System.out.println("LiveReload: Port 35729"); System.out.println("Automatic Restart: Active"); System.out.println("==============================="); } @EventListener public void onRestart(RestartEvent event) { System.out.println("Application restarted at: " + new Date()); System.out.println("Restart cause: " + event.getSource()); } }

Conclusion

Spring Boot DevTools significantly enhances developer productivity by providing:

  • Fast application restarts without full JVM shutdown
  • Live browser reloading for static content changes
  • Sensible development defaults for caching and debugging
  • Remote development support for cloud environments
  • Customizable restart triggers and exclusions

By properly configuring and utilizing DevTools, you can achieve sub-second restart times and a seamless development experience that closely mirrors production behavior while maintaining development convenience.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper