Springboot - DevTools - 在重建项目时并不总是映射RestController

Mae*_*lig 7 spring intellij-idea maven spring-boot

我正在使用带有maven的SpringBoot 1.3.5.

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.3.5.RELEASE</version>
</parent>
Run Code Online (Sandbox Code Playgroud)

和devtools

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
</dependency>
Run Code Online (Sandbox Code Playgroud)

我正在使用2014年以前的Intellij IDEA 2016.2同样的问题.

我正在从Intellij Idea运行我的springboot应用程序,首先启动一切都很好并且可以工作,我可以访问我的静态页面和我的2个Rest Controller工作.

2016-08-18 15:27:58.771  INFO 26626 --- [  restartedMain] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@469d0c02: startup date [Thu Aug 18 15:27:57 CEST 2016]; root of context hierarchy
2016-08-18 15:27:58.789  INFO 26626 --- [  restartedMain] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/authentication/introspect],methods=[GET]}" onto public com.myapp.models.TokenIntrospection com.myapp.resources.AuthenticationResources.introspectToken(java.lang.String)
2016-08-18 15:27:58.790  INFO 26626 --- [  restartedMain] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/configuration],methods=[GET]}" onto public com.myapp.models.AppConfiguration com.myapp.resources.ConfigurationResources.getConfiguration()
2016-08-18 15:27:58.792  INFO 26626 --- [  restartedMain] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2016-08-18 15:27:58.793  INFO 26626 --- [  restartedMain] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
Run Code Online (Sandbox Code Playgroud)

因为简单的"Make Project"不能很好地用于静态重载,所以我使用"Rebuild Project",有时,当app重启时,我没有映射我的控制器,有时一个丢失,有时两个都丢失了.

我对此没有任何线索:(

编辑

@Morfic解决方案不起作用,因此我使用Intellij本地服务器来提供静态内容和gulp-livereload而不是spring-dev-tools.

IJ本地服务器

当我处于开发模式时,我只需要在JS中管理REST调用,因为REST资源在localhost:8080但我在localhost上的静态:63342,并在我的springboot中启用CORS(在属性文件中使用一个标志来启用CORS) ).

@Configuration
public class CorsConfig extends WebMvcConfigurerAdapter {

    @Value("${cors.enabled}")
    private boolean corsEnabled;

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        super.addCorsMappings(registry);
        if(corsEnabled) {
            registry.addMapping("/**")
                    .allowedOrigins("*")
                    .allowedMethods("GET", "PUT", "POST", "DELETE", "OPTIONS")
                    .allowedHeaders("Origin", "X-Requested-With", "Content-Type", "Accept", "Authorization")
                    .allowCredentials(true)
                    .maxAge(3600L);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

所以仍有待解决问题的问题.

Mor*_*fic 8

我只是设法用一个简单的hello-world服务重复这个并使用Rebuild project了几次,因为它只会偶尔复制一次.我的预感是开发工具找出了在IJ有机会完全清理和重建之前发生的变化.可能一旦资源被发布,并且在从我看到的输出目录编译类之前,dev-tools开始重新加载尚未出现的类 ...

在这个假设之后,我查看了日志和类时间戳,并且dev-tools开始重新加载上下文的时间与我的类写入磁盘的时间之间存在大约1s的差距.显然我的@PostConstruct日志都没有出现,而且spring的autoconfig没有找到我的课程......

作为解决方法,您可以使用触发器文件.可以按照链接或application.properties文件中的建议将其添加为home-dir中的全局配置.此外,由于对此文件的任何更改(创建,删除,修改)都会触发重新启动,并Rebuild project清除输出目录,因此您必须定义一个额外的路径来查找此文件.因此,假设我们有一个常规的IJ spring boot运行配置,其中包含以下2个application.properties:

# name of the file to trigger a restart
spring.devtools.restart.trigger-file=restarttrigger

# where else to look for it. Also . evaluates to the app's base dir
spring.devtools.restart.additional-paths=.
Run Code Online (Sandbox Code Playgroud)

...一旦你看到IJ完成构建过程,转到app root dir并添加或删除你的触发器文件,具体取决于它是否已经存在,这将导致重启.我已经测试了几次,没有尝试到目前为止失败.下面是手动重启过程的简短视频演示:

IJ  - 启动开发工具手动重启

有几种方法可以自动执行此过程.除了在IJ中定义一个人工制品并使用后期处理ant任务生成文件之外,你可以使用maven(你已经在使用它)生成这样一个文件,但缺点是你必须使用maven compile而不是Rebuild project因为IJ在进行重建时不会调用maven(或者我还没有发现如何做到这一点).请在下面找到一个基于以下事实的简单配置:

(注意:在Maven 2.0.5及更高版本中,绑定到阶段的多个目标的执行顺序与它们在POM中声明的顺序相同,但不支持同一插件的多个实例.同一插件的多个实例被分组一起执行并在Maven 2.0.11及更高版本中订购).

因此,编译器插件默认绑定到compile阶段,因此我们添加一个小任务来使用该文件(或其他任何东西)生成触发器文件application.properties

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
        <plugin>
            <!-- first, compile all we need -->
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.3</version>
        </plugin>
        <plugin>
            <!-- then, generate the trigger-file so dev-tools will restart -->
            <artifactId>maven-antrun-plugin</artifactId>
            <version>1.8</version>
            <executions>
                <execution>
                    <phase>compile</phase>
                    <configuration>
                        <tasks>
                            <copy file="${project.basedir}/src/main/resources/application.properties"
                                  toFile="${project.basedir}/restarttrigger" overwrite="true" />
                        </tasks>
                    </configuration>
                    <goals>
                        <goal>run</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>
Run Code Online (Sandbox Code Playgroud)

进一步更新:

查看源代码FileSystemWatcher.scan(),有一个do-while循环可以解释为:虽然自上次检查后文件系统仍在进行更改,但等待(可配置)时间并再次验证

private void scan() throws InterruptedException {
    Thread.sleep(this.pollInterval - this.quietPeriod);
    Map<File, FolderSnapshot> previous;
    Map<File, FolderSnapshot> current = this.folders;
    do {
        previous = current;
        current = getCurrentSnapshots();
        Thread.sleep(this.quietPeriod);
    }
    while (isDifferent(previous, current));
    if (isDifferent(this.folders, current)) {
        updateSnapshots(current.values());
    }
}
Run Code Online (Sandbox Code Playgroud)

根据文档,quietPeriod可以通过spring.devtools.restart.quiet-period属性进行配置,但是根据上面提到的来源,它必须是小于可pollInterval配置的值spring.devtools.restart.poll-interval.因此,玩弄设置,我得到了一个不错的结果:

# Amount of time (in milliseconds) to wait between polling for classpath changes.
spring.devtools.restart.poll-interval=3000

# Amount of quiet time (in milliseconds) required without any classpath changes before a restart is triggered.
spring.devtools.restart.quiet-period=2999
Run Code Online (Sandbox Code Playgroud)

最后,您应该能够将这些值调整为最适合您的值.

尽管如此,如果您正在修改的源是静态资源,例如FE GUI页面,并且根据您的要求,也许最好使用从其位置为它们提供服务的工具,例如节点或类似的简单http服务器......