springboot实战篇 - Hot swapping

前言

你是否因为每次改动代码还需要启停项目而烦恼?你是否因为每次测试环境更新代码都需要重新打包而烦恼?今天它来了,它就是我们今天的主题Hot-Swapping(热拔插)。

Hot Swapping

我们先看下官方介绍(大家可以根据自己依赖的springboot版本自行下载对应的文档,因为我写这篇文章时用的是2.2.7.RELEASE,这里就拿这个版本的文档进行讲解)。

7.5. Hot Swapping
Since Spring Boot applications are just plain Java applications, JVM hot-swapping should work out of the box. JVM hot swapping is somewhat limited with the bytecode that it can replace. For a more complete solution, JRebel can be used.
The spring-boot-devtools module also includes support for quick application restarts. See the Developer Tools section later in this chapter and the Hot swapping “How-to” for details.

通过上面官方的描述我们可以知道springboot 有个模块叫spring-boot-devtools可以支持应用的快速重启。

如何启用spring-boot-devtools

我本地是Maven项目,开发工具是IDEA,这里就以这两个作为基准进行讲解

1.添加maven依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <optional>true</optional> <!--optional表示该依赖只在本项目传递,若其他项目依赖本项目,该依赖无效-->
    </dependency>
</dependencies>

我们来看下官方的注意事项

Developer tools are automatically disabled when running a fully packaged application. If your application is launched from java -jar or if it is started from a special classloader, then it is considered a “production application”. If that does not apply to you (i.e. if you run your application from a container), consider excluding devtools or set the -Dspring.devtools.restart.enabled=false system property.
Flagging the dependency as optional in Maven or using a custom developmentOnly configuration in Gradle (as shown above) is a best practice that prevents devtools from being transitively applied to other modules that use your project.

1)首先SpringBoot-devtools 当运行全包形式的程序时, 就是通过 java -jar xxxx.jar来启动项目时,会自动禁用开发者工具。 还有一种情况就是在container下面,我们需要通过系统参数来禁用: -Dspring.devtools.restart.enabled=false。
2)在Maven中将依赖项标记为optional或在Gradle中使用自定义developmentOnly配置,可以防止将devtools依赖传递到其他模块。

2.默认属性

默认情况下SpringBoot-devtools会禁用缓存选项,我们无须通过application.properties手动去禁用一些模板引擎(比如thymeleaf等),官方文档指出禁用清单如下:


    static {

        Map<String, Object> properties = new HashMap<>();

        properties.put("spring.thymeleaf.cache", "false");

        properties.put("spring.freemarker.cache", "false");

        properties.put("spring.groovy.template.cache", "false");

        properties.put("spring.mustache.cache", "false");

        properties.put("server.servlet.session.persistent", "true");

        properties.put("spring.h2.console.enabled", "true");

        properties.put("spring.resources.cache.period", "0");

        properties.put("spring.resources.chain.cache", "false");

        properties.put("spring.template.provider.cache", "false");

        properties.put("spring.mvc.log-resolved-exception", "true");

        properties.put("server.error.include-stacktrace", "ALWAYS");

        properties.put("server.servlet.jsp.init-parameters.development", "true");

        properties.put("spring.reactor.debug", "true");

        PROPERTIES = Collections.unmodifiableMap(properties);

    }


如果不想使用上面的默认配置,可以在application.properties中通过spring.devtools.add-properties = false来禁用。

3.验证

1)简易的controller

package com.sunyard.eshop.controller;

import com.sunyard.eshop.entity.Order;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class OrderController {
    
    @RequestMapping("/order/{id}")
    public Order getOrder(@PathVariable("id") String orderId){
        Order order = new Order();
        order.setOrderId(orderId);
        order.setPhoneNo("139xxxxxx");
        order.setProductName("IPHONE");
        return order;
    }


}

启动服务,页面输入http://localhost:8080/order/3,得到结果

image.png

这时候我们不要启停服务,直接修改代码

package com.sunyard.eshop.controller;

import com.sunyard.eshop.entity.Order;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class OrderController {

    @RequestMapping("/order/{id}")
    public Order getOrder(@PathVariable("id") String orderId){
        Order order = new Order();
        order.setOrderId(orderId);
        order.setPhoneNo("158xxxxxx");
        order.setProductName("HUAWEI");
        return order;
    }


}

然后build -> build project (快捷键 ctrl + F9),控制台会出现如下信息

image.png

这时候我们再次访问http://localhost:8080/order/3
image.png

此时productName变成了HUAWEI,phoneNo变成了158xxxxxx,这样我们就完成了本地代码的热加载,简单吧!

如何远程启动SpringBoot-devtools

有时候我们本地改动一些代码之后,不想重新打包到测试环境服务器上,这时候我们可以采用SpringBoot-devtools的远程热拔插。

1.修改pom文件

pom文件新增 <excludeDevtools>false</excludeDevtools>

<plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludeDevtools>false</excludeDevtools>  <!--新增-->
                </configuration>
            </plugin>

2.修改application.properties

spring.devtools.remote.secret=mysecret   

mycecret是自定义密码,client和remote server之间同步代码时就使用这个密码进行校验

3.修改本地服务启动配置

指定Main Class为org.springframework.boot.devtools.RemoteSpringApplication
指定Program arguments为http://127.0.0.1:8080 即远端服务的地址(自行更换地址)


image.png

4.验证

1)先将服务打包,并启动,用来模拟远程端(我是用一台电脑做测试,远程端口号是8080),这时候远程端会有这样一句话输出,代表我们远程端的配置生效了

image.png

2)通过IDEA启动本地服务(我是一台电脑测试,这里本地代码启动端口是8081),这时候控制台有如下的输出,代表已经连接上了远程服务。
image.png

3)访问远程服务 http://localhost:8080/order/4
image.png

4)修改本地服务代码,并通过build -> build project (crtl + F9)重新加载代码

package com.sunyard.eshop.controller;

import com.sunyard.eshop.entity.Order;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class OrderController {

    @RequestMapping("/order/{id}")
    public Order getOrder(@PathVariable("id") String orderId){
        Order order = new Order();
        order.setOrderId(orderId);
        order.setPhoneNo("199xxxxxx");
        order.setProductName("XIAOMI");
        return order;
    }
}

这时候发现服务端自动重启了一次


image.png

5)再次访问远程服务 http://localhost:8080/order/4,通过截图可以看到,远程服务已经同步了本地的代码,无须我们再次打包上传到远程服务了。

image.png

结束语

本次springboot热拔插的分享就到此结束了,以后我会不定时更新springboot使用的一些小技巧,欢迎大家一起学习 ^ ^

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容