輕量級 ioc 框架 loveqq,支持接口上傳 jar 格式的 starter 啟動器并支持熱加載其中的 bean
熱加載 starter 啟動器代碼示例:
package com.kfyty.demo;import com.kfyty.loveqq.framework.boot.K;
import com.kfyty.loveqq.framework.boot.context.ContextRefresher;
import com.kfyty.loveqq.framework.core.autoconfig.annotation.Autowired;
import com.kfyty.loveqq.framework.core.autoconfig.annotation.BootApplication;
import com.kfyty.loveqq.framework.core.autoconfig.annotation.Component;
import com.kfyty.loveqq.framework.core.autoconfig.condition.annotation.ConditionalOnMissingBean;
import com.kfyty.loveqq.framework.core.lang.JarIndexClassLoader;
import com.kfyty.loveqq.framework.core.utils.IOC;
import com.kfyty.loveqq.framework.web.core.annotation.GetMapping;
import com.kfyty.loveqq.framework.web.core.annotation.RequestMapping;
import com.kfyty.loveqq.framework.web.core.annotation.RestController;
import com.kfyty.loveqq.framework.web.core.autoconfig.annotation.EnableWebMvc;
import com.kfyty.loveqq.framework.web.core.multipart.MultipartFile;
import lombok.extern.slf4j.Slf4j;import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.security.cert.Extension;
import java.util.Collections;
import java.util.UUID;
import java.util.jar.JarFile;@Slf4j
@EnableWebMvc
@RestController
@BootApplication
@RequestMapping(expose = true) // 自動暴露 public 方法為 POST http 接口
public class Main {@Autowiredprivate Extension extension;/*** 測試接口*/@GetMappingpublic String sayHello() {return extension.getId();}/*** 加載插件** @param jar jar 包 啟動器* @return 上傳后的 jar 包絕對路徑,卸載啟動器時需要提供該返回值*/public String loadPlugin(MultipartFile jar) throws Exception {// 保存到本地String filePath = "D:\\temp\\jar\\" + UUID.randomUUID().toString().replace("-", "") + "\\" + jar.getOriginalFilename();File jarFile = new File(filePath);jar.transferTo(jarFile);// 添加到框架 ClassLoaderJarIndexClassLoader classLoader = (JarIndexClassLoader) IOC.class.getClassLoader();classLoader.addJarIndex(Collections.singletonList(new JarFile(jarFile)));// 刷新上下文ContextRefresher.refresh(IOC.getApplicationContext());return jarFile.getAbsolutePath();}/*** 卸載啟動器** @param jarPath {@link #loadPlugin(MultipartFile)} 的返回值*/public String unloadPlugin(String jarPath) throws Exception {// 構建 File 對象File jarFile = new File(jarPath);// 從框架 ClassLoader 移除JarIndexClassLoader classLoader = (JarIndexClassLoader) IOC.class.getClassLoader();classLoader.removeJarIndex(Collections.singletonList(new JarFile(jarFile)));// 刷新上下文ContextRefresher.refresh(IOC.getApplicationContext());return "ok";}public static void main(String[] args) throws Exception {K.run(Main.class, args);}/*** 默認實現*/@Component@ConditionalOnMissingBean(Extension.class)public static class DefaultExtension implements Extension {@Overridepublic String getId() {return "default";}@Overridepublic boolean isCritical() {return false;}@Overridepublic byte[] getValue() {return new byte[0];}@Overridepublic void encode(OutputStream out) throws IOException {}}
}
然后,新建一個項目,添加如下類:
package com.kfyty.graal.example;import com.kfyty.loveqq.framework.core.autoconfig.annotation.Component;import java.io.IOException;
import java.io.OutputStream;
import java.security.cert.Extension;/*** 動態加載示例實現*/
@Component
public class ExampleExtension implements Extension {@Overridepublic String getId() {return "example";}@Overridepublic boolean isCritical() {return false;}@Overridepublic byte[] getValue() {return new byte[0];}@Overridepublic void encode(OutputStream out) throws IOException {}
}
并在 k.factories 中添加:
com.kfyty.loveqq.framework.core.autoconfig.annotation.EnableAutoConfiguration=com.kfyty.graal.example.ExampleExtension
然后打成 jar 包,就是一個啟動器了。
接著啟動第一段代碼的 main 方法后:
先訪問:http://localhost:8080/sayHello,將返回 default
然后使用 postman 上傳啟動器 jar 包:http://127.0.0.1:8080/loadPlugin,此時將動態加載上傳的啟動器,并刷新 ioc 容器
然后再訪問:http://localhost:8080/sayHello,將返回 example,原因是加載了新的啟動器,條件注解生效,實現類變化了!
然后再訪問:http://127.0.0.1:8080/unloadPlugin,將第二步的返回值作為入參傳入,此時將卸載啟動器,并刷新 ioc 容器
然后再訪問:http://localhost:8080/sayHello,將返回 default,原因是卸載了之前加載的啟動器,條件注解生效,實現類又變化了!
從而實現了啟動器的熱加載,感興趣的同學可以試一下。
gitee/github/gitcode: loveqq-framework