Springboot使用SPI注册bean到spring容器的示例代码

2022-11-13 18:11:05 代码 示例 容器

新建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 {
 
    }
 
    
    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;
    }
 
    
    private Map<String, Object> loadExtensionClass(String type) {
        Map<String, Object> extensionClasses = new HashMap<>();
        loadDirectory(extensionClasses, SPI_DIRECTORY, type);
        return extensionClasses;
    }
 
    
    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());
        }
    }
 
    
    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 {
 
    
    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;
    }
 
    
    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;
    }
 
    
    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内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

相关文章