专栏首页ThoughtWorksGradle Spring Intellij Idea下热部署实现“敏捷”开发 | TW洞见

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),作者:思特沃克

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

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

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

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 为什么我们要尝试Kotlin

    技术雷达:对Android的完美支持为迅速发展的Kotlin语言提供了额外的推动力,我们也正在密切关注Kotlin / Native(基于LLVM,可以将Kot...

    ThoughtWorks
  • TW洞见〡亲爱的,今天你“债”了吗?

    文章作者来自ThoughtWorks:刘彩红,图片来自网络。 打开百度百科,债务指债户还债的义务,有时也指所欠的债及为了清偿所有的债务而工作。 ...

    ThoughtWorks
  • Spring Batch在大型企业中的最佳实践|洞见

    在大型企业中,由于业务复杂、数据量大、数据格式不同、数据交互格式繁杂,并非所有的操作都能通过交互界面进行处理。而有一些操作需要定期读取大批量的数据,然后进行一系...

    ThoughtWorks
  • 小白学Python | 最简单的Django 简明教程

    web框架: 别人已经设定好的一个web网站模板,你学习它的规则,然后“填空”或“修改”成你自己需要的样子。

    小小科
  • 小白学Python | 最简单的Django 简明教程

    ? 一、Django简介 1. web框架介绍 具体介绍Django之前,必须先介绍WEB框架等概念。 web框架: 别人已经设定好的一个web网站模板,你学...

    小小科
  • 记录一次Hexo的崩溃修复

    这次崩溃的原因是安装主题的时候手贱终止了一次然后就出了问题,导致hexo的各种命令如clean、generate、server等一直报警告并且导致网页资源的渲染...

    impressionyang
  • LeetCode.601.Human_Traffic_of_Stadium

    https://leetcode.com/problems/human-traffic-of-stadium/

    大数据工程师-公子
  • 【精选案例】赛程魔方3D旋转界面设计

    《腾讯网UED体验设计之旅》是腾讯网UED十年精华输出的干货型读物,从用户研究、创意剖析、绘制方法、项目管理等实体案例出发,解读了网媒用户分析与研究方法、门户网...

    博文视点Broadview
  • MSVC/MinGW中导入glog静态库的正确方式

    在linux下使用glog静态库,编译连接时只要加上glog静态库文件(.a)就可以了,在windows环境,我也同样的办法在自己的项目中使用glog静态库,用...

    用户1148648
  • SQL 高级查询 ——(层次化查询,递归)

    层次化结构可以理解为树状数据结构,由节点构成。比如常见的组织结构由一个总经理,多个副总经理,多个部门部长组成。再比如在生产制造中一件产品会有多个子零件组成。举个...

    Lenis

扫码关注云+社区

领取腾讯云代金券