一个基于Spring实现的热更新插件开发框架

2024-09-11 15:19:47 浏览数 (3)

前言

对于其他解释性语言来说,热更新根本不是什么事,但对于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控制器

代码语言:javascript复制
@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

代码语言:javascript复制
@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定时任务

代码语言:javascript复制
@Slf4j
@Component
public class TaskDemo {

    @Scheduled(cron = "0/1 * * * * ?")
    public void task() {
        log.info("task starting");
    }
}

定时任务运行

使用JNI加载dll

代码语言:javascript复制
    @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文件中增加:

代码语言:javascript复制
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>2.0.39</version>
        </dependency>

在Controller 中使用第三方依赖包中的类

代码语言:javascript复制
    @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来使用数据库

代码语言:javascript复制
@Mapper
public interface TestMapper extends BaseMapper<User> {

    User selectByIdXml(@Param("id") Integer id);
}

编写xml,xml要放在跟Mapper类一起

如果放到resources中,路径要跟Mapper类一样

代码语言:javascript复制
<?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 中增加接口

代码语言:javascript复制
    @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

代码语言:javascript复制
@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

插件事件监听

在主程序中编写如下代码:

代码语言:javascript复制
/**
 * 插件事件监听
 * @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

1 人点赞