Springboot使用SPI注冊(cè)bean到spring容器的示例代碼
新建resources/META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.ExtensionLoader
新建META-INF/vtest/全路徑接口名
mysqlDriver=com.MysqlDriver oracleDriver=com.OracleDriver
MyDriver接口
public interface MyDriver { void getConnect(); }
MysqlDriver實(shí)現(xiàn)
public class MysqlDriver implements MyDriver{ @Override public void getConnect() { System.out.println("connect"); } }
OracleDriver實(shí)現(xiàn)
public class OracleDriver implements MyDriver{ @Override public void getConnect() { System.out.println("connect"); } }
import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.*; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.util.StringUtils; import java.io.BufferedReader; import java.io.File; import java.io.InputStreamReader; import java.lang.reflect.Field; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public class ExtensionLoader implements BeanDefinitionRegistryPostProcessor, ApplicationContextAware { ApplicationContext context; BeanDefinitionRegistry beanDefinitionRegistry; ConcurrentHashMap<Class<?>, Map<String, Object>> EXTENSIONS = new ConcurrentHashMap<>(); private static final String SPI_DIRECTORY = "META-INF/vtest/"; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.context = applicationContext; ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) context; beanDefinitionRegistry = (DefaultListableBeanFactory) configurableApplicationContext.getBeanFactory(); } @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException { try { ClassLoader classLoader = DefaultListableBeanFactory.class.getClassLoader(); URL resource; File[] files; if (classLoader != null) { resource = classLoader.getResource(this.SPI_DIRECTORY); } else { resource = ClassLoader.getSystemResource(this.SPI_DIRECTORY); } files = new File(resource.getFile()).listFiles(); for (int i = 0; i < files.length; i++) { Class<?> clazz = Class.forName(files[i].getName(), true, classLoader); EXTENSIONS.putIfAbsent(clazz, loadExtensionClass(clazz.getName())); } } catch (Exception e) { e.printStackTrace(); } } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { } /** * 獲取某個(gè)接口類型對(duì)應(yīng)的實(shí)現(xiàn) * * @param type * @return */ public Map<String, Object> getExtensions(Class type) { if (null == type) { throw new IllegalArgumentException("Extension Class is null"); } if (!type.isInterface()) { throw new IllegalArgumentException("Extension Class is not an interface"); } Map<String, Object> loader = EXTENSIONS.get(type); if (loader == null) { synchronized (ExtensionLoader.class) { loader = EXTENSIONS.get(type); if (loader == null) { EXTENSIONS.putIfAbsent(type, loadExtensionClass(type.getName())); loader = EXTENSIONS.get(type); } } } return loader; } /** * 從擴(kuò)展文件中加載類 * * @param type * @return */ private Map<String, Object> loadExtensionClass(String type) { Map<String, Object> extensionClasses = new HashMap<>(); loadDirectory(extensionClasses, SPI_DIRECTORY, type); return extensionClasses; } /** * 加載文件夾 * * @param extensionClasses * @param dir * @param type */ private void loadDirectory(Map<String, Object> extensionClasses, String dir, String type) { String fileName = dir + type; try { Enumeration<URL> urls; ClassLoader classLoader = DefaultListableBeanFactory.class.getClassLoader(); if (classLoader != null) { urls = classLoader.getResources(fileName); } else { urls = ClassLoader.getSystemResources(fileName); } if (urls != null) { while (urls.hasMoreElements()) { URL resourcesURL = urls.nextElement(); loadResources(extensionClasses, classLoader, resourcesURL); } } } catch (Throwable t) { } } private void loadResources(Map<String, Object> extensionClasses, ClassLoader classLoader, URL resourceURL) { try { try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) { String line; while ((line = reader.readLine()) != null) { final int ci = line.indexOf('#'); if (ci >= 0) { line = line.substring(0, ci); } line = line.trim(); if (line.length() > 0) { try { String name = null; int i = line.indexOf('='); if (i > 0) { name = line.substring(0, i).trim(); line = line.substring(i + 1).trim(); } if (line.length() > 0) { loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name); } } catch (Throwable t) { IllegalStateException e = new IllegalStateException("Failed to load extension class (class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t); } } } } } catch (Throwable t) { } } private void loadClass(Map<String, Object> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) { if (StringUtils.isEmpty(name)) { throw new IllegalStateException("No such extension name for the class " + name + " in the config " + resourceURL); } Object o = extensionClasses.get(name); if (o == null) { Object bean = injectBeanToSpring(name, clazz); extensionClasses.put(name, bean); } else { throw new IllegalStateException("Duplicate extension name " + name + " on " + clazz.getName() + " and " + clazz.getName()); } } /** * 動(dòng)態(tài)注入bean到spring容器 * * @param name * @param obj * @return */ private Object injectBeanToSpring(String name, Class<?> obj) { String beanName = name; BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(obj); GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition(); definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_NAME); beanDefinitionRegistry.registerBeanDefinition(beanName, definition); // TODO: 2020/1/9 這里動(dòng)態(tài)注入的bean并未將內(nèi)部的@Autowired的bean依賴注入進(jìn)去,如何解決? // 通過反射設(shè)置@Autowired標(biāo)記的字段的值 Object bean = context.getBean(beanName); Field[] declaredFields = obj.getDeclaredFields(); for (Field field : declaredFields) { if (field.isAnnotationPresent(Autowired.class)) { Object aClass = context.getBean(field.getType()); ReflectHelper.setFieldValue(bean, field.getName(), aClass); } } return bean; } }
public class ReflectHelper { /** * 利用反射獲取指定對(duì)象的指定屬性 * * @param obj 目標(biāo)對(duì)象 * @param fieldName 目標(biāo)屬性 * @return 目標(biāo)屬性的值 */ public static Object getFieldValue(Object obj, String fieldName) { Object result = null; Field field = ReflectHelper.getField(obj, fieldName); if (field != null) { field.setAccessible(true); try { result = field.get(obj); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } return result; } /** * 利用反射獲取指定對(duì)象里面的指定屬性 * * @param obj 目標(biāo)對(duì)象 * @param fieldName 目標(biāo)屬性 * @return 目標(biāo)字段 */ private static Field getField(Object obj, String fieldName) { Field field = null; for (Class<?> clazz = obj.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) { try { field = clazz.getDeclaredField(fieldName); break; } catch (NoSuchFieldException e) { //這里不用做處理,子類沒有該字段可能對(duì)應(yīng)的父類有,都沒有就返回null。 } } return field; } /** * 利用反射設(shè)置指定對(duì)象的指定屬性為指定的值 * * @param obj 目標(biāo)對(duì)象 * @param fieldName 目標(biāo)屬性 * @param fieldValue 目標(biāo)值 */ public static void setFieldValue(Object obj, String fieldName, Object fieldValue) { Field field = ReflectHelper.getField(obj, fieldName); if (field != null) { try { field.setAccessible(true); field.set(obj, fieldValue); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } }
Controller:
@RestController @RequestMapping("/t") @Api(value = "測試服務(wù)", description = "") public class TestController { // 切換不同的服務(wù) @Autowired @Qualifier("mysqlDriver") private MyDriver myDriver; @ApiOperation(value = "測試", notes = "基于SPRING BOOT實(shí)現(xiàn)的JAVA SPI機(jī)制的DEMO") @GetMapping("/spi") public String test() { myDriver.getConnect(); return "ok"; } }
到此這篇關(guān)于Springboot使用SPI注冊(cè)bean到spring容器的文章就介紹到這了,更多相關(guān)Springboot注冊(cè)bean內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
淺談spring中的default-lazy-init參數(shù)和lazy-init
下面小編就為大家?guī)硪黄獪\談spring中的default-lazy-init參數(shù)和lazy-init。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-04-04關(guān)于Java利用反射實(shí)現(xiàn)動(dòng)態(tài)運(yùn)行一行或多行代碼
這篇文章主要介紹了關(guān)于Java利用反射實(shí)現(xiàn)動(dòng)態(tài)運(yùn)行一行或多行代碼,借鑒了別人的方法和書上的內(nèi)容,最后將題目完成了,和大家一起分享以下解決方法,需要的朋友可以參考下2023-04-04Spring中@Transactional用法詳細(xì)介紹
這篇文章主要介紹了Spring中@Transactional用法詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下2017-02-02SpringBoot+URule實(shí)現(xiàn)可視化規(guī)則引擎的方法示例
規(guī)則引擎其實(shí)是一種組件,它可以嵌入到程序當(dāng)中,將程序復(fù)雜的判斷規(guī)則從業(yè)務(wù)代碼中剝離出來,使得程序只需要關(guān)心自己的業(yè)務(wù),而不需要去進(jìn)行復(fù)雜的邏輯判斷,本文給大家介紹了SpringBoot+URule實(shí)現(xiàn)可視化規(guī)則引擎的方法示例,需要的朋友可以參考下2024-12-12Java如何向主函數(shù)main中傳入?yún)?shù)
這篇文章主要介紹了Java如何向主函數(shù)main中傳入?yún)?shù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02解決SpringBoot返回結(jié)果如果為null或空值不顯示處理問題
這篇文章主要介紹了解決SpringBoot返回結(jié)果如果為null或空值不顯示處理問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07