关于Spring Loaded

最近在倒腾Spring全家桶的应用,在阅读官方文档19.5节的时候无意中发现了了一个被称作hot-swapping的技术,这类在java5代理技术出现之后出现的字节码热交换技术其实也不是新鲜事,但是对于spring这样application动不动启动10来秒的框架如若使用热交换对开发效率无疑有一个质的提升,于是就根据文档踩了下坑。

引入

Maven

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

Gradle

如果作为gradle引入的话要稍微麻烦一点
首先需要加入依赖

dependencies {
compile "org.springframework.boot:spring-boot-gradle-plugin"
compile 'org.springframework:springloaded'
}
idea {
module {
inheritOutputDirs = false
outputDir = file("$buildDir/classes/main/")
}
}
buildscript {
repositories {
jcenter()
}
}

运行时加入VM参数

-javaagent:{path_to_jar}\springloaded-1.2.5.RELEASE.jar -noverify

如果想完成静态资源静态替换,则需要在application.properties中加入

spring.freemarker.cache=false //此为freemarker配置,其他模版引擎请参照文档

完成上述配置之后便可以使用热交换插件了

使用

1.使用调试模式运行Spring boot application,笔者使用的IDEA为图示按钮


2.修改字节码后使用上图左侧make按钮(默认快捷键alt+f9)编译新的项目,由于插件的存在,会自动将output中的代码进行热替换而无需重新启动整个application。

3.模版引擎在make之后也能马上变化(非Spring Loaded特性,无论是否配置Spring Loaded,静态资源替换在make候会自动生效)

1.如果是一般类/方法修改热替换,代理技术会起到很好的作用,但是需要注意我是我们使用的是Ioc容器,如springmvc的注解映射依赖于启动时扫描,假如替换了映射的类,springmvc并不知道这一变化,而会寻找之前的方法,这时候就会抛出找不到方法异常
简单看下示例

@RequestMapping("/finduser/{username}")
@ResponseBody
public User findUser(@PathVariable String username){
User user=userService.findByUsername(username);
return user;
}

此时我们如果把该方法改为

@RequestMapping("/finduser/{username}")
@ResponseBody
public String findUser(@PathVariable String username){
return "ok";
}

虽然字节码被热替换了,但是框架抛出了异常

java.lang.NoSuchMethodError: {packagename}.UserController.findUser(Ljava/lang/String;)L{packagename}/domain/User;

总结

对于普通类的修改,spring loaded确实是是一个很好的工具,整个make/替换过程短暂极大提升了开发效率,但对于部分ioc容器,依旧需要重启appliction才能完全看到修改后的代码。