詳解如何給Sprintboot應(yīng)用添加插件機(jī)制
場(chǎng)景
想要讓boot應(yīng)用增加插件能力,擴(kuò)展restful api。插件可以由第三方開(kāi)發(fā)
要解決的問(wèn)題
- 第三方的api需要和主應(yīng)用使用相同的pom依賴
- 第三方的api獨(dú)立打包成jar包,并按照命名規(guī)則取名
- 第三方的api需要再boot應(yīng)用之外的獨(dú)立存儲(chǔ)中放置(部署)
- 第三方的api jar 包的加載時(shí)機(jī)及方式
方案
- 獨(dú)立的依賴管理 pom 第三方插件繼承此pom 統(tǒng)一依賴
- api jar 包放置到特定路徑。由boot 啟動(dòng)時(shí)加載。(也可以熱加載,但實(shí)現(xiàn)方式復(fù)雜一些)
- 技術(shù)點(diǎn) classloader 加載插件 jar, 類型需要添加到spring bean 中統(tǒng)一管理生命周期。
下面是classloader的實(shí)現(xiàn)
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; } } }
啟動(dòng)時(shí)將類加入到spring中
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); } }
運(yùn)行時(shí)將類加載到spring中,此時(shí)需要用ApplicationContextAware
@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); } }
做一個(gè)運(yùn)行時(shí)加載的入口
@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");
到此這篇關(guān)于詳解如何給Sprintboot應(yīng)用添加插件機(jī)制的文章就介紹到這了,更多相關(guān)Sprintboot應(yīng)用添加插件機(jī)制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring?Boot實(shí)現(xiàn)文件上傳的兩種方式總結(jié)
應(yīng)用開(kāi)發(fā)過(guò)程中,文件上傳是一個(gè)基礎(chǔ)的擴(kuò)展功能,它的目的就是讓大家共享我們上傳的文件資源,下面這篇文章主要給大家總結(jié)介紹了關(guān)于Spring?Boot實(shí)現(xiàn)文件上傳的兩種方式,需要的朋友可以參考下2023-05-05java?spring?validation?自動(dòng)、手動(dòng)校驗(yàn)
HibernateValidator簡(jiǎn)化了Java開(kāi)發(fā)中的參數(shù)校驗(yàn)過(guò)程,提供自動(dòng)和手動(dòng)兩種校驗(yàn)方式,通過(guò)引入相關(guān)依賴并使用@Validated注解,可以實(shí)現(xiàn)自動(dòng)校驗(yàn),手動(dòng)校驗(yàn)則需要使用ValidatorUtils類,此方法有效減少代碼重復(fù),提高開(kāi)發(fā)效率2024-09-09java servlet結(jié)合Oracle搭建java的web開(kāi)發(fā)環(huán)境
今天我將與大家分享一下我學(xué)JAVA WEB寫(xiě)的一些小實(shí)例 ,我個(gè)人是不太喜歡書(shū)本上的晦澀的概念的,所以我花了更多的時(shí)間在一些應(yīng)用實(shí)例上,我覺(jué)得這樣的學(xué)習(xí)方式很適合我,由簡(jiǎn)到繁,由淺入深2015-12-12Spring編程式和聲明式事務(wù)實(shí)例講解小結(jié)
這篇文章主要介紹了Spring編程式和聲明式事務(wù)實(shí)例講解小結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-07-07Java數(shù)據(jù)結(jié)構(gòu)之位圖的簡(jiǎn)單實(shí)現(xiàn)和使用
位圖,?是一種非常常見(jiàn)的結(jié)構(gòu),?它使用每個(gè)二進(jìn)制位來(lái)存放一個(gè)值的狀態(tài),?就類似于?Java?當(dāng)中?HashSet?存儲(chǔ)元素的功能。本文主要來(lái)介紹一下位圖的簡(jiǎn)單實(shí)現(xiàn)和使用,需要的可以參考一下2023-05-05Java實(shí)現(xiàn)簡(jiǎn)單的學(xué)生教師管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡(jiǎn)單的學(xué)生教師管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02java線程池ExecutorService超時(shí)處理小結(jié)
使用ExecutorService時(shí),設(shè)置子線程執(zhí)行超時(shí)是一個(gè)常見(jiàn)需求,本文就來(lái)詳細(xì)的介紹一下ExecutorService超時(shí)的三種方法,感興趣的可以了解一下2024-09-09