Skip to content

Commit 6df1084

Browse files
committed
Allow Devtools to be enabled irrespective of the launching ClassLoader
Closes gh-21424
1 parent 30bc0ad commit 6df1084

File tree

4 files changed

+59
-7
lines changed

4 files changed

+59
-7
lines changed

spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/DefaultRestartInitializer.java

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,38 @@ public URL[] getInitialUrls(Thread thread) {
4343
}
4444

4545
/**
46-
* Returns if the thread is for a main invocation. By default checks the name of the
47-
* thread and the context classloader.
46+
* Returns if the thread is for a main invocation. By default {@link #isMain(Thread)
47+
* checks the name of the thread} and {@link #isDevelopmentClassLoader(ClassLoader)
48+
* the context classloader}.
4849
* @param thread the thread to check
4950
* @return {@code true} if the thread is a main invocation
51+
* @see #isMainThread
52+
* @see #isDevelopmentClassLoader(ClassLoader)
5053
*/
5154
protected boolean isMain(Thread thread) {
52-
return thread.getName().equals("main")
53-
&& thread.getContextClassLoader().getClass().getName().contains("AppClassLoader");
55+
return isMainThread(thread) && isDevelopmentClassLoader(thread.getContextClassLoader());
56+
}
57+
58+
/**
59+
* Returns whether the given {@code thread} is considered to be the main thread.
60+
* @param thread the thread to check
61+
* @return {@code true} if it's the main thread, otherwise {@code false}
62+
* @since 2.4.0
63+
*/
64+
protected boolean isMainThread(Thread thread) {
65+
return thread.getName().equals("main");
66+
}
67+
68+
/**
69+
* Returns whether the given {@code classLoader} is one that is typically used during
70+
* development.
71+
* @param classLoader the ClassLoader to check
72+
* @return {@code true} if it's a ClassLoader typically used during development,
73+
* otherwise {@code false}
74+
* @since 2.4.0
75+
*/
76+
protected boolean isDevelopmentClassLoader(ClassLoader classLoader) {
77+
return classLoader.getClass().getName().contains("AppClassLoader");
5478
}
5579

5680
/**

spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/RestartApplicationListener.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -64,7 +64,24 @@ private void onApplicationStartingEvent(ApplicationStartingEvent event) {
6464
// It's too early to use the Spring environment but we should still allow
6565
// users to disable restart using a System property.
6666
String enabled = System.getProperty(ENABLED_PROPERTY);
67-
if (enabled == null || Boolean.parseBoolean(enabled)) {
67+
RestartInitializer restartInitializer = null;
68+
if (enabled == null) {
69+
restartInitializer = new DefaultRestartInitializer();
70+
}
71+
else if (Boolean.parseBoolean(enabled)) {
72+
restartInitializer = new DefaultRestartInitializer() {
73+
74+
@Override
75+
protected boolean isDevelopmentClassLoader(ClassLoader classLoader) {
76+
return true;
77+
}
78+
79+
};
80+
logger.info(LogMessage.format(
81+
"Restart enabled irrespective of application packaging due to System property '%s' being set to true",
82+
ENABLED_PROPERTY));
83+
}
84+
if (restartInitializer != null) {
6885
String[] args = event.getArgs();
6986
DefaultRestartInitializer initializer = new DefaultRestartInitializer();
7087
boolean restartOnInitialize = !AgentReloader.isActive();

spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/restart/RestartApplicationListenerTests.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,14 @@ void disableWithSystemProperty(CapturedOutput output) {
8888
assertThat(output).contains("Restart disabled due to System property");
8989
}
9090

91+
@Test
92+
void enableWithSystemProperty(CapturedOutput output) {
93+
System.setProperty(ENABLED_PROPERTY, "true");
94+
testInitialize(false);
95+
assertThat(Restarter.getInstance()).hasFieldOrPropertyWithValue("enabled", true);
96+
assertThat(output).contains("Restart enabled irrespective of application packaging due to System property");
97+
}
98+
9199
private void testInitialize(boolean failed) {
92100
Restarter.clearInstance();
93101
RestartApplicationListener listener = new RestartApplicationListener();

spring-boot-project/spring-boot-docs/src/docs/asciidoc/using-spring-boot.adoc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,10 @@ To include devtools support, add the module dependency to your build, as shown i
542542

543543
NOTE: Developer tools are automatically disabled when running a fully packaged application.
544544
If your application is launched from `java -jar` or if it is started from a special classloader, then it is considered a "`production application`".
545-
If that does not apply to you (i.e. if you run your application from a container), consider excluding devtools or set the `-Dspring.devtools.restart.enabled=false` system property.
545+
You can control this behavior by using the `spring.devtools.restart.enabled` system property.
546+
To enable devtools, irrespective of the classloader used to launch your application, set the `-Dspring.devtools.restart.enabled=true` system property.
547+
This must not be done in a production environment where running devtools is a security risk.
548+
To disable devtools, exclude the dependency or set the `-Dspring.devtools.restart.enabled=false` system property.
546549

547550
TIP: Flagging the dependency as optional in Maven or using the `developmentOnly` configuration in Gradle (as shown above) prevents devtools from being transitively applied to other modules that use your project.
548551

0 commit comments

Comments
 (0)