Springboot - DevTools - 在重建项目时并不总是映射RestController [英] Springboot - DevTools - RestController not always mapped when Rebuild Project

查看:384
本文介绍了Springboot - DevTools - 在重建项目时并不总是映射RestController的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

 < parent> 
< groupId> org.springframework.boot< / groupId>
< artifactId> spring-boot-starter-parent< / artifactId>
< version> 1.3.5.RELEASE< / version>
< / parent>

和devtools

 <依赖性> 
< groupId> org.springframework.boot< / groupId>
< artifactId> spring-boot-devtools< / artifactId>
< / dependency>

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



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

  2016-08-18 15:27:58.771 INFO 26626 --- [restartedMain] swsmmaRequestMappingHandlerAdapter:寻找@ControllerAdvice:org.springframework.boot.context.embedded .AnnotationConfigEmbeddedWebApplicationContext @ 469d0c02:启动日期[Thu Aug 18 15:27:57 CEST 2016];上下文层次结构
2016-08-18 15:27:58.789 INFO 26626 --- [restartedMain] swsmmaRequestMappingHandlerMapping:将{[/ authentication / introspect],methods = [GET]}映射到公共com上。 myapp.models.TokenIntrospection com.myapp.resources.AuthenticationResources.introspectToken(java.lang.String)
2016-08-18 15:27:58.790 INFO 26626 --- [restartedMain] swsmmaRequestMappingHandlerMapping:Mapped{[ / configuration],methods = [GET]}on public com.myapp.models.AppConfiguration com.myapp.resources.ConfigurationResources.getConfiguration()
2016-08-18 15:27:58.792 INFO 26626 --- [restartedMain] swsmmaRequestMappingHandlerMapping:将{[/ error]}映射到公共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] swsmmaRequestMappingHandlerMapping:Mapped{ [/ error],produ = [text / html]}on public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet) .http.HttpServletResponse)

因为简单的Make Project不能很好地用于静态重载,我使用重建项目,有时,当应用程序重新启动时,我没有映射我的控制器,有时一个丢失,有时两个都丢失。



我不知道对此有任何线索:(



编辑



@Morfic解决方案没有work,所以我使用Intellij本地服务器来提供静态内容和gulp-livereload而不是spring-dev-tools。





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

  @Configuration 
公共类CorsConfig扩展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 (真)
.maxAge(3600L);
}
}
}

所以问题仍然悬而未决工作解决方案。

解决方案

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



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



作为一种解决方法,你可以使用



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


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


因此,编译器插件是由默认绑定到编译阶段,因此我们添加一个小任务来生成启动/ docs / current / reference / html / using-boot-devtools.html#using-boot-devtools-restart-triggerfilerel =nofollow noreferrer> trigger-file application.propertie s 文件(或其他任何内容)

 < build> 
< plugins>
< plugin>
< groupId> org.springframework.boot< / groupId>
< artifactId> spring-boot-maven-plugin< / artifactId>
< / plugin>
< plugin>
<! - 首先,编译我们需要的所有 - >
< artifactId> maven-compiler-plugin< / artifactId>
< version> 3.3< / version>
< / plugin>
< plugin>
<! - 然后,生成触发器文件,以便dev-tools重启 - >
< 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} / restarttriggeroverwrite =true/> ;
< / tasks>
< / configuration>
< goals>
< goal> run< / goal>
< / goals>
< / execution>
< / executions>
< / plugin>
< / plugins>
< / build>






进一步更新:



查看 FileSystemWatcher.scan() ,有一个 do-while 循环,可以解释为:,自上次检查后文件系统上仍有变化,等待(可配置)时间并再次验证

  private void scan()抛出InterruptedException {
Thread.sleep(this.pollInterval - this.quietPeriod);
Map< File,FolderSnapshot>以前;
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());
}
}

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

 #等待的时间量(以毫秒为单位)轮询类路径更改。 
spring.devtools.restart.poll-interval = 3000

#触发重启之前没有任何类路径更改所需的安静时间(以毫秒为单位)。
spring.devtools.restart.quiet-period = 2999

最后,你应该能够将这些值调整到最适合您的值。



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


I'm using SpringBoot 1.3.5 with maven.

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.3.5.RELEASE</version>
</parent>

And devtools

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
</dependency>

I'm using Intellij IDEA 2016.2, previously 2014 with same problem.

I'm running my springboot app from Intellij Idea, first launch everything is well loaded and works, I can access my static pages and my 2 Rest Controllers work.

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)

Because simply "Make Project" doesn't work well for static reload, I use "Rebuild Project" and sometimes, when app restart, I don't have my controllers mapped, sometimes one is missing, sometimes both are missing.

I don't have any clue about this :(

EDIT

@Morfic solutions did not work, so I used the Intellij local server to serve static contents and gulp-livereload instead of spring-dev-tools.

I just had to manage REST calls in JS when I'm in dev mode because REST resources are on localhost:8080 but my statics on localhost:63342, and enable CORS in my springboot (with a flag in properties file to enable CORS or not).

@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);
        }
    }
}

So question still pending for a working solution.

解决方案

I just managed to reproduce this with a simple hello-world service and using Rebuild project a couple of times because it only reproduces once in a while. My hunch is that dev-tools figure out a change has occurred before IJ has a chance to completely clean & rebuild. Probably as soon as the resources are published and before the classes are compiled from what I've seen looking at the output dir, dev-tools starts reloading the classes which are not yet present...

Following this supposition, I looked at the logs and classes timestamps and there's about a 1s gap between the time dev-tools starts reloading the context and the time my classes are written to disk. Obviously none of my @PostConstruct logs appear and spring's autoconfig does not find my classes...

As a workaround, you could use a trigger-file. Either add it as a global config in your home-dir as suggested in the link, or in your application.properties file. Additionally since any change to this file (creation, deletion, modification) would trigger a restart, and Rebuild project cleans the output directory, you'd have to define an additional path to look for this file. Thus, suppose we have a regular IJ spring boot run configuration, with the following 2 in the 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=.

... once you see IJ completing the build process, go to the app root dir and add or delete your trigger file, depending on whether it's already there or not, which should cause a restart. I've tested this a few times with no attempt failing so far. Below a short vide demo of the manual restart process:

There are a couple of ways to automate this process. Aside from defining an artefact in IJ and using a post-process ant task to generate file, you can use maven (which you're already using) to generate such a file, but the downside is that you have to use maven compile instead of Rebuild project because IJ will not invoke maven when doing the rebuild (or I haven't found out how to do that yet). Please find below a simple configuration based on the fact that:

(Note: In Maven 2.0.5 and above, multiple goals bound to a phase are executed in the same order as they are declared in the POM, however multiple instances of the same plugin are not supported. Multiple instances of the same plugin are grouped to execute together and ordered in Maven 2.0.11 and above).

As such, the compiler plugin is by default bound to the compile phase, so we add a small task to generate the trigger-file using the application.properties file (or anything else)

<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>


Further update:

Looking at the sources of FileSystemWatcher.scan(), there's a do-while loop which can be interpreted as: while there are still changes ongoing on the file system since the previous check, wait for a (configurable) time and verify again

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());
    }
}

As per the documentation, the quietPeriod is configurable through the spring.devtools.restart.quiet-period property, but also as per the above mention source, it must be a value smaller than pollInterval configurable through spring.devtools.restart.poll-interval. Thus, playing around with the settings, I got a decent result with:

# 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

In the end, you should be able to adjust those values to what suits you best.

Nonetheless, if the sources you're modifying are static resources such as FE GUI pages and depending on your requirements, perhaps it's better to use a tool which serves them from their location, such as node or a similar simple http server...

这篇关于Springboot - DevTools - 在重建项目时并不总是映射RestController的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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