Java 运行Jar时强制启用spring boot DevTools
我正在Docker容器中运行我的spring boot应用程序,试图使用 国家 运行完全打包的应用程序时,开发人员工具将自动禁用。如果您的应用程序是使用java-jar启动的,或者是使用特殊的类加载器启动的,那么它被认为是“生产应用程序”Java 运行Jar时强制启用spring boot DevTools,java,spring,spring-boot,Java,Spring,Spring Boot,我正在Docker容器中运行我的spring boot应用程序,试图使用 国家 运行完全打包的应用程序时,开发人员工具将自动禁用。如果您的应用程序是使用java-jar启动的,或者是使用特殊的类加载器启动的,那么它被认为是“生产应用程序” 有没有办法强制启用DevTools?解决方案很粗略,因此您可以决定它是否对您有利最终解决方案是本帖的最后一部分 很难给出解决方案,我首先需要解释一下我是如何做到的。首先,为什么在IDE外部启动时未启用livereload: 了解正在发生的事情 (1) Loc
有没有办法强制启用DevTools?解决方案很粗略,因此您可以决定它是否对您有利最终解决方案是本帖的最后一部分 很难给出解决方案,我首先需要解释一下我是如何做到的。首先,为什么在IDE外部启动时未启用livereload:
了解正在发生的事情 (1) LocalDevToolsAutoConfiguration配置取决于
@ConditionalOnInitializedRestarter/OnInitializedRestarterCondition
:
@Configuration
@ConditionalOnInitializedRestarter
@EnableConfigurationProperties(DevToolsProperties.class)
public class LocalDevToolsAutoConfiguration {
...
(2) OnInitializedRestarterCondition检索重启器实例并检查其是否为null,否则Restarter.getInitialUrls()
返回null。在我的例子中,restarter.getInitialUrls()
返回null
class OnInitializedRestarterCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
Restarter restarter = getRestarter();
if (restarter == null) {
return ConditionOutcome.noMatch("Restarter unavailable");
}
if (restarter.getInitialUrls() == null) {
return ConditionOutcome.noMatch("Restarter initialized without URLs");
}
return ConditionOutcome.match("Restarter available and initialized");
}
(3) initialURL
在Restarter.class
中通过defaultrestaritializer.getinitialURL(…)
thread.getContextClassLoader()
.getClass().getName()包含(“AppClassLoader”)
只有在从Eclipse(可能是任何IDE?+springboot maven插件?)运行时才是真的。重述:
- isMain()返回false
- 初始URL未初始化
- 未配置条件LocalDevToolsAtoConfiguration
- 没有利弗雷洛德
解决方案: 通过创建自己的AppClassLoader类加载器,确保类加载器名称为“AppClassLoader”。 在spring boot main的第一行,将classloader替换为您的:
URLClassLoader originalClassLoader = (URLClassLoader)Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(new CustomAppClassLoader(originalClassLoader));
我们的自定义类加载器实现仅委托给原始类加载器:
public class CustomAppClassLoader extends URLClassLoader{
private URLClassLoader contextClassLoader;
public CustomAppClassLoader(URLClassLoader contextClassLoader) {
super(contextClassLoader.getURLs(), contextClassLoader.getParent());
this.contextClassLoader = contextClassLoader;
}
public int hashCode() {
return contextClassLoader.hashCode();
}
public boolean equals(Object obj) {
return contextClassLoader.equals(obj);
}
public InputStream getResourceAsStream(String name) {
return contextClassLoader.getResourceAsStream(name);
}
public String toString() {
return contextClassLoader.toString();
}
public void close() throws IOException {
contextClassLoader.close();
}
public URL[] getURLs() {
return contextClassLoader.getURLs();
}
public Class<?> loadClass(String name) throws ClassNotFoundException {
return contextClassLoader.loadClass(name);
}
public URL findResource(String name) {
return contextClassLoader.findResource(name);
}
public Enumeration<URL> findResources(String name) throws IOException {
return contextClassLoader.findResources(name);
}
public URL getResource(String name) {
return contextClassLoader.getResource(name);
}
public Enumeration<URL> getResources(String name) throws IOException {
return contextClassLoader.getResources(name);
}
public void setDefaultAssertionStatus(boolean enabled) {
contextClassLoader.setDefaultAssertionStatus(enabled);
}
public void setPackageAssertionStatus(String packageName, boolean enabled) {
contextClassLoader.setPackageAssertionStatus(packageName, enabled);
}
public void setClassAssertionStatus(String className, boolean enabled) {
contextClassLoader.setClassAssertionStatus(className, enabled);
}
public void clearAssertionStatus() {
contextClassLoader.clearAssertionStatus();
}
公共类CustomAppClassLoader扩展URLClassLoader{
私有URLClassLoader contextClassLoader;
公共CustomAppClassLoader(URLClassLoader contextClassLoader){
超级(contextClassLoader.getURLs(),contextClassLoader.getParent());
this.contextClassLoader=contextClassLoader;
}
公共int hashCode(){
返回contextClassLoader.hashCode();
}
公共布尔等于(对象obj){
返回contextClassLoader.equals(obj);
}
公共InputStream getResourceAsStream(字符串名称){
返回contextClassLoader.getResourceAsStream(名称);
}
公共字符串toString(){
返回contextClassLoader.toString();
}
public void close()引发IOException{
contextClassLoader.close();
}
公共URL[]获取URL(){
返回contextClassLoader.getURLs();
}
公共类loadClass(字符串名称)引发ClassNotFoundException{
返回contextClassLoader.loadClass(名称);
}
公共URL findResource(字符串名称){
返回contextClassLoader.findResource(名称);
}
公共枚举findResources(字符串名称)引发IOException{
返回contextClassLoader.findResources(名称);
}
公共URL getResource(字符串名称){
返回contextClassLoader.getResource(名称);
}
公共枚举getResources(字符串名称)引发IOException{
返回contextClassLoader.getResources(名称);
}
public void setDefaultAssertionStatus(启用布尔值){
contextClassLoader.setDefaultAssertionStatus(已启用);
}
public void setPackageAssertionStatus(字符串packageName,启用布尔值){
contextClassLoader.setPackageAssertionStatus(packageName,已启用);
}
public void setClassAssertionStatus(字符串类名称,启用布尔值){
contextClassLoader.setClassAssertionStatus(类名称,已启用);
}
公共无效clearAssertionStatus(){
contextClassLoader.clearAssertionStatus();
}
}
我尽可能多地配置了CustomAppClassLoader(使用原始类加载器中的'url'和'parent'调用super),但无论如何,我仍然将所有公共方法委托给原始类加载器
它对我有用。现在,更好的问题是我真的想要这个:)
更好的选择
我认为Spring Cloud的RestartEndpoint
是一个更好的选择:
但是,
RestartEndPoint
不会检测类路径中的更改。请确保重新打包的存档中包含devtools,如下所示:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludeDevtools>false</excludeDevtools>
</configuration>
</plugin>
</plugins>
</build>
org.springframework.boot
springbootmaven插件
假的
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludeDevtools>false</excludeDevtools>
</configuration>
</plugin>
</plugins>
</build>