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)去,如何解決?
// 通過(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) {
//這里不用做處理,子類沒(méi)有該字段可能對(duì)應(yīng)的父類有,都沒(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)文章
MyBatis-Plus邏輯刪除和字段自動(dòng)填充的實(shí)現(xiàn)
本文主要介紹了MyBatis-Plus邏輯刪除和字段自動(dòng)填充的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08
解決SpringMvc中普通類注入Service為null的問(wèn)題
這篇文章主要介紹了解決SpringMvc中普通類注入Service為null的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
2022年最新java?8?(?jdk1.8u321)安裝圖文教程
這篇文章主要介紹了2022年最新java?8?(?jdk1.8u321)安裝圖文教程,截止2022年1月,官方出的jdk1.8目前已更新到8u321的版本,本文通過(guò)圖文并茂的形式給大家介紹安裝過(guò)程,需要的朋友可以參考下2022-08-08
MyBatis-Plus實(shí)現(xiàn)多數(shù)據(jù)源的示例代碼
這篇文章主要介紹了MyBatis-Plus實(shí)現(xiàn)多數(shù)據(jù)源的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
Java非法字符: ‘\ufeff‘問(wèn)題及說(shuō)明
這篇文章主要介紹了Java非法字符: ‘\ufeff‘問(wèn)題及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-02-02

