运行Jar时强制启用spring-boot DevTools [英] Force enable spring-boot DevTools when running Jar

查看:228
本文介绍了运行Jar时强制启用spring-boot DevTools的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在Docker容器中运行我的spring-boot应用程序,试图使用 remote LiveReload

I am running my spring-boot app in a Docker container, trying to use remote LiveReload.

spring-boot DevTools文档声明


运行完全打包的应用程序时,会自动禁用开发人员工具。如果你的应用程序是使用java -jar启动的,或者它是使用特殊的类加载器启动的,那么它被认为是生产应用程序。

Developer tools are automatically disabled when running a fully packaged application. If your application is launched using java -jar or if it’s started using a special classloader, then it is considered a "production application".

有没有办法强制启用DevTools?

Is there any way to force the enabling of DevTools?

推荐答案

解决方案很粗略,所以你决定它是否对你有好处。 最终的解决方案是这篇文章的最后一部分

The solution is sketchy, so you decide if it's good for you. The final solution is the last part of this post

很难抛出解决方案,我首先需要解释一下我是如何到达那里的。首先,为什么在IDE外部启动时不启用livereload:

It's hard to just throw the solution, I first need to explain how I got there. First, why livereload is not enabled when launching outside the IDE:

了解正在发生的事情

(1)LocalDevToolsAutoConfiguration配置以 @ConditionalOnInitializedRestarter / OnInitializedRestarterCondition

(1) LocalDevToolsAutoConfiguration configuration is conditional on @ConditionalOnInitializedRestarter/OnInitializedRestarterCondition:

    @Configuration
    @ConditionalOnInitializedRestarter
    @EnableConfigurationProperties(DevToolsProperties.class)
    public class LocalDevToolsAutoConfiguration {
    ...

(2)OnInitializedRestarterCondition检索Restarter实例并检查它是否为null否则 restarter .getInitialUrls()返回null。就我而言, restarter.getInitialUrls()返回null。

(2) OnInitializedRestarterCondition retrieves a Restarter instance and checks if it's null otherwise restarter.getInitialUrls() returns null. In my case, restarter.getInitialUrls() was returning 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) initialUrls Restarter.class中初始化通过 DefaultRestartInitializer.getInitialUrls(..)

class Restarter{
    this.initialUrls = initializer.getInitialUrls(thread);
}

class DefaultRestartInitializer{
    @Override
    public URL[] getInitialUrls(Thread thread) {
        if (!isMain(thread)) {
            return null;
        }
        for (StackTraceElement element : thread.getStackTrace()) {
            if (isSkippedStackElement(element)) {
                return null;
            }
        }
        return getUrls(thread);
    }

    protected boolean isMain(Thread thread) {
    return thread.getName().equals("main") && thread.getContextClassLoader()
            .getClass().getName().contains("AppClassLoader");
    }
}





thread.getContextClassLoader()
.getClass()。getName()。contains(AppClassLoader)

thread.getContextClassLoader() .getClass().getName().contains("AppClassLoader")


仅在从Eclipse(可能是任何IDE?+ springboot-maven-plugin?)运行时才是真的。回顾:

is only true when running from Eclipse (possibly any IDE? + springboot-maven-plugin?). To Recap:


  • isMain()返回false;

  • isMain() returns false;

initialUrls未初始化;

the initialUrls is not initialized;

未配置条件LocalDevToolsAutoConfiguration;

the conditional LocalDevToolsAutoConfiguration is not configured;

没有livereload。

no livereload.

解决方案:

通过创建自己的AppClassLoader类加载器,确保类加载器名称为AppClassLoader。
在spring-boot main的第一行,用你的替换classloader:

Make sure the classloader name is "AppClassLoader" by Creating your own AppClassLoader classloader. At the very first line of your spring-boot main, replace the classloader with yours:

URLClassLoader originalClassLoader = (URLClassLoader)Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(new CustomAppClassLoader(originalClassLoader));

我们的自定义类加载器实现只需委托原始实现:

Our custom classloader implementation simply delegates to the original one:

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(用原始类加载器中的'url'和'parent'调用super),但无论如何我仍然将所有公共方法委托给原始的类加载器。

I configured CustomAppClassLoader as much as I could (calling super with 'url' and 'parent' from the original classloader), but anyway I still delegate all public methods to the original classloader.

它对我有用。现在,更好的问题是我真的想要这个:)

It works for me. Now, the better question is do I really want this :)

更好的选择

我认为 Spring Cloud的RestartEndpoint 是一个更好的选择:以编程方式重新启动Spring Boot应用程序
但是, RestartEndPoint 无法检测类路径中的更改。

I think Spring Cloud's RestartEndpoint is a better option: Programmatically restart Spring Boot application However, RestartEndPoint does not handle the detection of changes in the classpath.

这篇关于运行Jar时强制启用spring-boot DevTools的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆