Java 运行Jar时强制启用spring boot DevTools

Java 运行Jar时强制启用spring boot DevTools,java,spring,spring-boot,Java,Spring,Spring Boot,我正在Docker容器中运行我的spring boot应用程序,试图使用 国家 运行完全打包的应用程序时,开发人员工具将自动禁用。如果您的应用程序是使用java-jar启动的,或者是使用特殊的类加载器启动的,那么它被认为是“生产应用程序” 有没有办法强制启用DevTools?解决方案很粗略,因此您可以决定它是否对您有利最终解决方案是本帖的最后一部分 很难给出解决方案,我首先需要解释一下我是如何做到的。首先,为什么在IDE外部启动时未启用livereload: 了解正在发生的事情 (1) Loc

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

国家

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


有没有办法强制启用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>