一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - Spring Boot熱加載jar實現動態插件的思路

Spring Boot熱加載jar實現動態插件的思路

2022-02-22 00:38zlt2000 Java教程

本文主要介紹在 Spring Boot 工程中熱加載 jar 包并注冊成為 Bean 對象的一種實現思路,在動態擴展功能的同時支持在插件中注入主程序的 Bean 實現功能更強大的插件

Spring Boot熱加載jar實現動態插件的思路

一、背景

動態插件化編程是一件很酷的事情,能實現業務功能的 解耦 便于維護,另外也可以提升 可擴展性 隨時可以在不停服務器的情況下擴展功能,也具有非常好的 開放性 除了自己的研發人員可以開發功能之外,也能接納第三方開發商按照規范開發的插件。

常見的動態插件的實現方式有 SPI、OSGI 等方案,由于脫離了 Spring IOC 的管理在插件中無法注入主程序的 Bean 對象,例如主程序中已經集成了 Redis 但是在插件中無法使用。

本文主要介紹在 Spring Boot 工程中熱加載 jar 包并注冊成為 Bean 對象的一種實現思路,在動態擴展功能的同時支持在插件中注入主程序的 Bean 實現功能更強大的插件。

二、熱加載 jar 包

通過指定的鏈接或者路徑動態加載 jar 包,可以使用 URLClassLoaderaddURL 方法來實現,樣例代碼如下:

ClassLoaderUtil 類

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ClassLoaderUtil {
    public static ClassLoader getClassLoader(String url) {
        try {
            Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
            if (!method.isAccessible()) {
                method.setAccessible(true);
            }
            URLClassLoader classLoader = new URLClassLoader(new URL[]{}, ClassLoader.getSystemClassLoader());
            method.invoke(classLoader, new URL(url));
            return classLoader;
        } catch (Exception e) {
            log.error("getClassLoader-error", e);
            return null;
        }
    }
}

其中在創建 URLClassLoader 時,指定當前系統的 ClassLoader 為父類加載器 ClassLoader.getSystemClassLoader() 這步比較關鍵,用于打通主程序與插件之間的 ClassLoader ,解決把插件注冊進 IOC 時的各種 ClassNotFoundException 問題。

三、動態注冊 Bean

將插件 jar 中加載的實現類注冊到 Spring 的 IOC 中,同時也會將 IOC 中已有的 Bean 注入進插件中;分別在程序啟動時和運行時兩種場景下的實現方式。

3.1. 啟動時注冊 Bean

使用 ImportBeanDefinitionRegistrar 實現在 Spring Boot 啟動時動態注冊插件的 Bean,樣例代碼如下:

PluginImportBeanDefinitionRegistrar 類

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class PluginImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    private final String targetUrl = "file:/D:/SpringBootPluginTest/plugins/plugin-impl-0.0.1-SNAPSHOT.jar";
    private final String pluginClass = "com.plugin.impl.PluginImpl";
 
    @SneakyThrows
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        ClassLoader classLoader = ClassLoaderUtil.getClassLoader(targetUrl);
        Class<?> clazz = classLoader.loadClass(pluginClass);
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
        BeanDefinition beanDefinition = builder.getBeanDefinition();
        registry.registerBeanDefinition(clazz.getName(), beanDefinition);
    }
}

3.2. 運行時注冊 Bean

程序運行時動態注冊插件的 Bean 通過使用 ApplicationContext 對象來實現,樣例代碼如下:

?
1
2
3
4
5
6
7
8
@GetMapping("/reload")
public Object reload() throws ClassNotFoundException {
        ClassLoader classLoader = ClassLoaderUtil.getClassLoader(targetUrl);
        Class<?> clazz = classLoader.loadClass(pluginClass);
        springUtil.registerBean(clazz.getName(), clazz);
        PluginInterface plugin = (PluginInterface)springUtil.getBean(clazz.getName());
        return plugin.sayHello("test reload");
}

SpringUtil 類

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Component
public class SpringUtil implements ApplicationContextAware {
    private DefaultListableBeanFactory defaultListableBeanFactory;
    private ApplicationContext applicationContext;
 
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
        ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) applicationContext;
        this.defaultListableBeanFactory = (DefaultListableBeanFactory) configurableApplicationContext.getBeanFactory();
    }
 
    public void registerBean(String beanName, Class<?> clazz) {
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
        defaultListableBeanFactory.registerBeanDefinition(beanName, beanDefinitionBuilder.getRawBeanDefinition());
    }
 
    public Object getBean(String name) {
        return applicationContext.getBean(name);
    }
}

四、總結

本文介紹的插件化實現思路通過 共用 ClassLoader動態注冊 Bean 的方式,打通了插件與主程序之間的類加載器和 Spring 容器,使得可以非常方便的實現插件與插件之間和插件與主程序之間的 類交互,例如在插件中注入主程序的 Redis、DataSource、調用遠程 Dubbo 接口等等。

但是由于沒有對插件之間的 ClassLoader 進行 隔離 也可能會存在如類沖突、版本沖突等問題;并且由于 ClassLoader 中的 Class 對象無法銷毀,所以除非修改類名或者類路徑,不然插件中已加載到 ClassLoader 的類是沒辦法動態修改的。

所以本方案比較適合插件數據量不會太多、具有較好的開發規范、插件經過測試后才能上線或發布的場景。

五、完整 demo

https://github.com/zlt2000/springs-boot-plugin-test

到此這篇關于Spring Boot熱加載jar實現動態插件的思路的文章就介紹到這了,更多相關Spring Boot熱加載jar內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!

原文鏈接:https://blog.csdn.net/zlt2000/article/details/120820770

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 亚洲国产成人精品不卡青青草原 | 久久99r66热这里有精品 | 99视频精品全部 在线 | 国产成人影院 | 波多野结衣中文字幕在线 | 国产精品人人视频 | 亚洲国产成人精品无码区APP | 91视频破解版| 国产精品国产三级在线专区 | 草莓视频幸福宝 | 万域之王在线观看 | 果冻传媒天美传媒在线小视频播放 | 久久国产主播福利在线 | a看片 | 欧美va在线播放免费观看 | 99热精品成人免费观看 | 激情亚洲天堂 | 国产二区三区 | 欧美成狂野欧美在线观看 | 羞羞漫画视频 | 蜜汁肉桃全文免费阅读 | 91普通话国产对白在线 | 黄网国产 | 2021最新国产成人精品视频 | 亚洲AV无码国产精品午夜久久 | 亚洲 日本 中文字幕 制服 | 精品国产乱码久久久久久软件 | 男人疯狂进女人下部视频动漫 | 亚洲精品www久久久久久久软件 | 久久久久久久电影 | 国产露脸对白刺激3p在线 | 网站在线观看 | 亚洲色图综合网 | 午夜AV内射一区二区三区红桃视 | 日韩精品福利视频一区二区三区 | 欧美女人p| 日韩欧美亚洲每日更新网 | 国产va免费精品高清在线观看 | 18亚洲chinese男男1069 | 美女被视频网站 | 国产高清在线精品一区二区三区 |