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è)接口類(lèi)型對(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ò)展文件中加載類(lèi)
*
* @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依賴(lài)注入進(jìn)去,如何解決?
// 通過(guò)反射設(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) {
//這里不用做處理,子類(lèi)沒(méi)有該字段可能對(duì)應(yīng)的父類(lèi)有,都沒(méi)有就返回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 = "測(cè)試服務(wù)", description = "")
public class TestController {
// 切換不同的服務(wù)
@Autowired
@Qualifier("mysqlDriver")
private MyDriver myDriver;
@ApiOperation(value = "測(cè)試", 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ī)?lái)一篇淺談spring中的default-lazy-init參數(shù)和lazy-init。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧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)行一行或多行代碼,借鑒了別人的方法和書(shū)上的內(nèi)容,最后將題目完成了,和大家一起分享以下解決方法,需要的朋友可以參考下2023-04-04
Spring中@Transactional用法詳細(xì)介紹
這篇文章主要介紹了Spring中@Transactional用法詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下2017-02-02
SpringBoot+URule實(shí)現(xiàn)可視化規(guī)則引擎的方法示例
規(guī)則引擎其實(shí)是一種組件,它可以嵌入到程序當(dāng)中,將程序復(fù)雜的判斷規(guī)則從業(yè)務(wù)代碼中剝離出來(lái),使得程序只需要關(guān)心自己的業(yè)務(wù),而不需要去進(jìn)行復(fù)雜的邏輯判斷,本文給大家介紹了SpringBoot+URule實(shí)現(xiàn)可視化規(guī)則引擎的方法示例,需要的朋友可以參考下2024-12-12
Java如何向主函數(shù)main中傳入?yún)?shù)
這篇文章主要介紹了Java如何向主函數(shù)main中傳入?yún)?shù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02
解決SpringBoot返回結(jié)果如果為null或空值不顯示處理問(wèn)題
這篇文章主要介紹了解決SpringBoot返回結(jié)果如果為null或空值不顯示處理問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07

