这篇文章主要介绍了Springboot使用SPI注册bean到spring容器,主要包括mydriver接口,mysqldriver实现过程,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
新建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实现
public class MysqlDriver implements MyDriver{
@Override
public void getConnect() {
System.out.println("connect");
}
}
OracleDriver实现
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 {
}
/**
* 获取某个接口类型对应的实现
*
* @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;
}
/**
* 从扩展文件中加载类
*
* @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());
}
}
/**
* 动态注入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 这里动态注入的bean并未将内部的@Autowired的bean依赖注入进去,如何解决?
// 通过反射设置@Autowired标记的字段的值
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 {
/**
* 利用反射获取指定对象的指定属性
*
* @param obj 目标对象
* @param fieldName 目标属性
* @return 目标属性的值
*/
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;
}
/**
* 利用反射获取指定对象里面的指定属性
*
* @param obj 目标对象
* @param fieldName 目标属性
* @return 目标字段
*/
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) {
//这里不用做处理,子类没有该字段可能对应的父类有,都没有就返回null。
}
}
return field;
}
/**
* 利用反射设置指定对象的指定属性为指定的值
*
* @param obj 目标对象
* @param fieldName 目标属性
* @param fieldValue 目标值
*/
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 = "测试服务", description = "")
public class TestController {
// 切换不同的服务
@Autowired
@Qualifier("mysqlDriver")
private MyDriver myDriver;
@ApiOperation(value = "测试", notes = "基于SPRING BOOT实现的JAVA SPI机制的DEMO")
@GetMapping("/spi")
public String test() {
myDriver.getConnect();
return "ok";
}
}
到此这篇关于Springboot使用SPI注册bean到spring容器的文章就介绍到这了,更多相关Springboot注册bean内容请搜索编程学习网以前的文章希望大家以后多多支持编程学习网!
沃梦达教程
本文标题为:Springboot使用SPI注册bean到spring容器的示例代码
基础教程推荐
猜你喜欢
- ConditionalOnProperty配置swagger不生效问题及解决 2023-01-02
- Java实现查找文件和替换文件内容 2023-04-06
- Java实现线程插队的示例代码 2022-09-03
- JDK数组阻塞队列源码深入分析总结 2023-04-18
- java实现多人聊天系统 2023-05-19
- java基础知识之FileInputStream流的使用 2023-08-11
- springboot自定义starter方法及注解实例 2023-03-31
- Java并发编程进阶之线程控制篇 2023-03-07
- Java数据结构之对象比较详解 2023-03-07
- Java文件管理操作的知识点整理 2023-05-19