前言
对于其他解释性语言来说,热更新根本不是什么事,但对于Java来说是多么的不容易,现在使用Java开发的热更新系统,基本使用JS编写脚本,然后用Java的JavaScript引擎来跑脚本。
spring-hot-plugin
现在有一款开源的Spring 热更新插件开发框架spring-hot-plugin,插件
- 支持编写各类Controller控制器,跟在Spring Boot 中写接口一样
 - 支持热加载普通类、各类Spring Bean,在插件里面写各类@Service、@Component
 - 支持热加载定时任务,在插件里面编写@Scheduled定时任务
 - 支持插件中使用第三方依赖,在插件中使用JNI加载dll,或者使用第三方jar依赖
 - 支持Mybatis、MybatisPlus,在插件中编写@Mapper,控制数据库
 - 提供主程序监听插件启动卸载事件,用于在主程序中跟随插件生命周期控制相关任务
 
下面演示一下支持的各个特性
Controller控制器
@Slf4j
@RestController
public class IndexController {
    @Resource
    private TestService testService;
    @GetMapping({"/hello"})
    public String hello() {
        return "Hello itsaysay!";
    }
	
    @GetMapping({"/hello/name"})
    public Person helloName() {
        Person person = new Person();
        person.setName("Human");
        String json = JSON.toJSONString(person);
        log.info(json);
        return person;
    }
}    
 
各类@Service、@Component
@Service
public class TestServiceImpl implements TestService {
    @Resource
    private TestMapper testMapper;
    @Resource
    private TestMapper2 testMapper2;
    @Override
    public User getUser(Integer id) {
        return testMapper.selectById(id);
    }
    @Override
    public User getUserXml(Integer id) {
        return testMapper.selectByIdXml(id);
    }
}
 
@Scheduled定时任务
@Slf4j
@Component
public class TaskDemo {
    @Scheduled(cron = "0/1 * * * * ?")
    public void task() {
        log.info("task starting");
    }
}
 
定时任务运行
 
使用JNI加载dll
    @GetMapping("/hik/sdk/init")
    public Boolean hikSDKInit() {
        //海康的SDK dll依赖了其他的dll,需要指定目录,放插件包没用
        //只有一个dll的sdk,不需要,可以放在插件包内
        //这是java native的限制
        HCNetSDK hCNetSDK = (HCNetSDK) Native.loadLibrary("HCNetSDK.dll", HCNetSDK.class);
        if (hCNetSDK == null) {
            return false;
        }
        return true;
    }
 
使用第三方Jar包
pom文件中增加:
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>2.0.39</version>
        </dependency>
 
在Controller 中使用第三方依赖包中的类
    @GetMapping({"/hello/name"})
    public Person helloName() {
        Person person = new Person();
        person.setName("Human");
        String json = JSON.toJSONString(person);
        log.info(json);
        return person;
    }
 
fastJson 加载后使用

编写@Mapper
这里我引入MP来使用数据库
@Mapper
public interface TestMapper extends BaseMapper<User> {
    User selectByIdXml(@Param("id") Integer id);
}
 
编写xml,xml要放在跟Mapper类一起
 
如果放到resources中,路径要跟Mapper类一样
 
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="csdn.itsaysay.demo.plugin.mapper.TestMapper">
    <select id="selectByIdXml" resultType="csdn.itsaysay.demo.plugin.bean.User">
        select * from usertb where id = #{id}
    </select>
</mapper>
 
在Controller 中增加接口
    @GetMapping("/mp/getUser/{id}")
    public User getUser(@PathVariable("id") Integer id) {
        return testService.getUser(id);
    }
    @GetMapping("/mp/getUserXml/{id}")
    public User getUserXml(@PathVariable("id") Integer id) {
        return testService.getUserXml(id);
    }
 
编写Service
@Service
public class TestServiceImpl implements TestService {
    @Resource
    private TestMapper testMapper;
    @Resource
    private TestMapper2 testMapper2;
    @Override
    public User getUser(Integer id) {
        return testMapper.selectById(id);
    }
    @Override
    public User getUserXml(Integer id) {
        return testMapper.selectByIdXml(id);
    }
}
 
MP调用:

调用XML中的 id

插件事件监听
在主程序中编写如下代码:
/**
 * 插件事件监听
 * @author jujun.chen
 */
@Slf4j
@Component
public class PluginListener implements vip.aliali.spring.plugin.listener.PluginListener {
    @Override
    public void startSuccess(PluginInfo pluginInfo) {
        log.info("{}--->启动成功", pluginInfo.getId());
    }
    @Override
    public void startFailure(PluginInfo pluginInfo, Throwable throwable) {
        log.info("{}--->启动失败", pluginInfo.getId());
    }
    @Override
    public void stopSuccess(PluginInfo pluginInfo) {
        log.info("{}--->停止成功", pluginInfo.getId());
    }
    @Override
    public void stopFailure(PluginInfo pluginInfo, Throwable throwable) {
        log.info("{}--->停止失败", pluginInfo.getId());
    }
}
 
通过调用安装、卸载插件接口,控制台打印相应日志

项目免不掉一些Bug,需要不断迭代更新
项目开源地址:https://github.com/jujunchen/spring-hot-plugin



![[Python学习日记-16] 细讲数据类型——元组](https://i-blog.csdnimg.cn/direct/882a9813424f45688c67f157251a511e.jpeg)















