Gradle Spring Intellij Idea下热部署实现“敏捷”开发 | TW洞见

今日洞见

文章作者来自ThoughtWorks:朱本威。

本文所有内容,包括文字、图片和音视频资料,版权均属ThoughtWorks公司所有,任何媒体、网站或个人未经本网协议授权不得转载、链接、转贴或以其他方式复制发布/发表。已经本网协议授权的媒体、网站,在使用时必须注明"内容来源:ThoughtWorks洞见",并指定原文链接,违者本网将依法追究责任。

#百万奖金有奖问答#程序员的什么最值钱?

是他/她们的聪明才智,简洁代码,惊艳的颜值,还是无与伦比的手速,都不是,是宝贵的时间。

如果,你有机会尝试纯前端的web开发,比如:NodeJS FrontEnd。你一定会非常喜欢watch这个功能,可以自动检测文件变化,然后自动完成编译和部署。

#另个一个百万奖金问题# 如果在Java/Spring环境下,也能做到Watch,该有多好!!!

那么,本篇文章不是什么高大上的抽象理论,也不是什么改变行业观念的大道理,从实际出发,就是要给你这个答案,只是希望在这三个环境(Gradle、Spring、Intellij Idea)下开发时,来给你节省一点点的时间。

什么是热部署:

It is the ability to change ON-THE-FLY what's currently deployed without redeploying it. Hot deployment is VERY hot for development.The time savings realized when your developers can simply run their build and have the new code auto-deploy instead of build, shutdown, startup is massive.

解决方案:

针对Spring的版本不同,将实现热部署的解决办法分为两个不同的方案:Spring3下Spring MVC + Jetty 和 Spring Boot,最后在给大家分享一个Gradle的Watch插件,来实现watch的功能。

Spring3下Spring MVC + Jetty的实现方式

首先来回顾一下Gradle的Jetty插件,Jetty插件提供两个重要方法:jettyRun和jettyRunWar。jettyRun会将一个已暴露(解包的)的web应用部署到嵌入式Jetty Web容器中。它不需要将web应用打包成一个war文件,目的是为了节省部署时间。jettyRunWar正好相反,是将一个War包部署到Web容器中。

jettyRun的好处是,你可以改变静态文件和JSP文件,而不需要重新启动服务器。

但是即便如此,对于日常开发还是不方便,因为开发过程中改动非常多的还有Java文件和资源配置文件,所以真正需要的是热部署。

jettyRun的Gradle API文档中有这么一句话:

Once started, the web container can be configured to run continuously, scanning for changes in the project and automatically performing a hot redeploy when necessary. This allows the developer to concentrate on coding changes to the project using their IDE of choice and have those changes immediately and transparently reflected in the running web container, eliminating development time that is wasted on rebuilding, reassembling and redeploying.

这句话简单总结就是Jetty提供实现热部署的特性,开发人员只需要专注于编写代码,减少重新构建,重新组装和重新部署所浪费的时间。那么如何配置来实现Jetty所提供的热部署呢?

Jetty插件提供了两个属性:

reload:The reload mode, which is either “automatic” or “manual”.

scanIntervalSeconds:The interval in seconds between scanning the web app for file changes. If file changes are detected, the web app is reloaded. Only relevant if reload is set to “automatic”. Defaults to 0, which disables automatic reloading.

读完上面两段,说明默认scanIntervalSeconds的配置是不支持自动重新载入变化文件的。所以,你需要修改Jetty插件的默认配置:

buildscript {  
    repositories { jcenter() }  
    dependencies {  
        classpath "org.springframework.boot:spring-boot-gradle-plugin:1.2.7.RELEASE"  
        classpath 'org.springframework:springloaded:1.2.4.RELEASE'  
    }  
}  
apply plugin: 'idea'  


idea {  
    module {  
        inheritOutputDirs = false  
        outputDir = file("$buildDir/classes/main/")  
        testOutputDir = file("$buildDir/classes/test/")  
    }  
}  

然后运行gradle jettyRun启动jetty容器,修改Java类/资源文件(resource下地文件),然后去页面验证变化,结果是没有变化。为什么?

原来,jetty监听的是build目录下的class和resource文件的变化,而不是源代码文件变化,也就说源代码内容改变了,但class文件没有变化,那么不会自动触发jetty重载变化文件,那么该怎么办?

另起一个窗口,手动运行一次gradlew compileJava或者gradlew processResources命令。就是这么简单,你已经实现了Spring 3下SpringMVC + Jetty的热部署了。

Spring Boot的实现方式 - Spring Reloaded

现在,越来越多的Spring应用直接使用Spring Boot作为框架,我司也是如此,Spring官方也意识针对热部署问题,提供了解决方案:Spring Reloaded。项目地址:spring-projects/spring-loaded · GitHub

Spring官方也很有意识,专门有一章节来介绍Hot Swapping:78. Hot swapping

它告诉如何将Spring Loaded和Gradle以及IntelliJ结合起来:

默认情况下,IntelliJ将Java类和资源文件编译到一个跟Gradle不同的位置,这会导致Spring Loaded监控失败,所以使用idea模块修改编译输出位置和Gradle一样,而且IntelliJ必须配置跟命令行Gradle任务相同的Java版本,且springloaded必须作为一个buildscript依赖被包含进去。

buildscript {  
    repositories { jcenter() }  
    dependencies {  
        classpath "org.springframework.boot:spring-boot-gradle-plugin:1.2.7.RELEASE"  
        classpath 'org.springframework:springloaded:1.2.4.RELEASE'  
    }  
}  
apply plugin: 'idea'  


idea {  
    module {  
        inheritOutputDirs = false  
        outputDir = file("$buildDir/classes/main/")  
        testOutputDir = file("$buildDir/classes/test/")  
    }  
}  

依赖下载完成之后,正常启动Spring Boot Run。

官方文档中存在的问题:

官方文档的springloaded版本是1.2.0.RELEASE,这个版本有问题,会出现:org.springsource.loaded.jvm.JVM : Problems copying method. Incompatible JVM? 报错。而且,如果你自己有仔细阅读官方文档的代码,你会发现官方少了一行testOutputDir的配置。官方文档上没有设置testOutputDir,这就会导致,intellij编译代码时,输出test下面的class到了out目录的main中。从而改变了Gradle默认的输出测试路径(main和test分开的),所以最好还是手动配置一下。

此时,如果你在应用启动的时候修改了Java代码,只需要点击Intellij的编译按钮,重新编译代码即可。

导致集成测试的问题:

由于这样配置之后,导致Intellij上进行make project,输出到build/classes下。这样做会导致集成测试有一个问题,运行集成测试的时候,我们常常需要使用properties文件和xml文件。如果之前运行过gradle build,而build目录下没有被clean,则gradle默认会将properties文件和xml文件放在build/resources下,这与Intellij的行为不同。而运行测试的时候,本来Intellij会默认先跑make project,但是由于build/classes已经有文件了,所以就skip了,于是导致Intellij找不到resource文件,因为Intellij要求的resource路径和gradle构建时输出的路径不同。

解决办法:手动点击Project Rebuild 或者 配置Intellij的Junit

可以手动点击Project Rebuild,它会清理Intellij的输出目录(也就是当前Gradle的classes目录),然后在make。又或者改变Intellij中Junit的配置,Junit会在运行测试之前,先跑make,可以然它在make之前先跑Gradle的clean任务。

Gradle下Watch大法实现方式 - Watch插件

最后,也是最关键的一点,Watch插件,通过上面的方法,实际已经实现热部署,只不过,每次变化都需要手动的触发compile和processResource,已经比重启服务器快了很多。

但这还不够,程序员就应该学会偷懒,我们应该自动化一切可以自动化且应该自动化得东西。

在github上有一个关注度并不是那么多的Watch插件:bluepapa32/gradle-watch-plugin · GitHub

他可以帮助你实现,任何文件变化的监控,并且在监控到变化之后,自动执行相应的Gradle任务。我们以Spring3下SpringMVC + Jetty方式为例:

buildscript {  
    repositories {  
        jcenter()  
    }  
    dependencies {  
        classpath 'com.bluepapa32:gradle-watch-plugin:0.1.3'  
    }  
}  

apply plugin: 'watch'  

watch {  
    java {  
        files files('src/main/java')  
        tasks 'compileJava'  
    }  
    resources {  
        files fileTree(dir: 'src/main/resources', include: '**/*.xml')  
        tasks 'processResources'  
    }  
}  

自动在Java或者resources文件有变化时,执行compileJava和processResources。那么,在Spring Boot下也是类似的一个配置。

自此,你再也不用重复可恶的三套动作以及无限的等待服务器启动:ctrl + c -> up key ->enter -> wait,专心实现Feature和修bug。

请让我安静地做一个只用写代码的程序员!

原文发布于微信公众号 - 思特沃克(ThoughtWorks)

原文发表时间:2016-02-28

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏JackieZheng

可视化工具gephi源码探秘(一)

  今天在老大和小梁的鼓舞和忽悠下(^_^),我决定还是把之前下载好的gephi源码好好利用起来,不在朝三暮四的想d3js或是什么vizster,用心去选择一个...

2245
来自专栏Java成长之路

idea创建maven项目时一直显示“Loading archetype list ”

1606
来自专栏java学习

Spring学习笔记1_Spring的概述

本章目录 Spring学习笔记1_Spring的概述 1.Spring介绍 2.Spring作用 3.Spring起源 4.Spring体系结构 5.Spri...

3006
来自专栏JavaQ

你需要知道的开源项目

Guava是Google的开源项目,它包含了Google工程师经常使用的内部核心库,例如集合、缓存、原语支持、并发库、通用注释、字符串处理、哈希、反射、I/O等...

1292
来自专栏Netkiller

跟我学 systemd

跟我学 systemd 摘要 我的系列文档 Netkiller Architect 手札Netkiller Developer 手札Netkiller PHP ...

37112
来自专栏后台及大数据开发

docker:(4)利用WebHook实现持续集成

在项目调试测试阶段,可能经常需要重复上面的步骤,以便将最新代码部署到特定环境供测试人员或其他人员使用

1081
来自专栏SpringBoot

SpringCloud Eureka

服务发现是基于微服务架构的关键原则之一。尝试配置每个客户端或某种形式的约定可能非常困难,可以非常脆弱。Netflix服务发现服务器和客户端是Eureka。可以将...

1081
来自专栏我是攻城师

Spring Boot如何集成duboo服务

3567
来自专栏Java职业技术分享

Spring Boot 面试题精华

Spring Boot 是微服务中最好的 Java 框架. 我们建议你能够成为一名 Spring Boot 的专家.

9464
来自专栏散尽浮华

jenkins配置记录(2)--代码发布流程

在我们的日常运维工作中,使用jenkins来完成业务代码发版上线是至关重要的一环。 前面已经提到在jenkins上添加用户权限的操作,今天重点说下如何在jenk...

5809

扫码关注云+社区