Spring Boot factories机制

Spring factories的加载方式类似于SPI

  1. 都是在顶层jar包中定义接口规范
  2. 具体接口实现交给项目按需加载
  3. 通过配置文件(spring.factories),定义对应接口的具体实现类
  4. 都是通过线程上下文类加载器的方式来加载具体的实现类
  5. 关于SPI的机制可以参考SPI加载机制和线程上下文类加载器

Spring Boot中具体的实现

private  Collection getSpringFactoriesInstances(Class type, Class[] parameterTypes, Object... args) {
    ClassLoader classLoader = getClassLoader();
    // Use names and ensure unique to protect against duplicates
    Set names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    List instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}
  1.  //获取线程上下文类加载器
     ClassLoader classLoader = getClassLoader();
    
  2.  //使用类加载器获取对应接口实现类的binary name,具体的实现在SpringFactoriesLoader类中完成。
     SpringFactoriesLoader.loadFactoryNames(type, classLoader)
    
    /**
    * General purpose factory loading mechanism for internal use within the framework.
    *
    * 

    {@code SpringFactoriesLoader} {@linkplain #loadFactories loads} and instantiates * factories of a given type from {@value #FACTORIES_RESOURCE_LOCATION} files which * may be present in multiple JAR files in the classpath. The {@code spring.factories} * file must be in {@link Properties} format, where the key is the fully qualified * name of the interface or abstract class, and the value is a comma-separated list of * implementation class names. For example: * *

    example.MyService=example.MyServiceImpl1,example.MyServiceImpl2
    * * where {@code example.MyService} is the name of the interface, and {@code MyServiceImpl1} * and {@code MyServiceImpl2} are two implementations. * * @author Arjen Poutsma * @author Juergen Hoeller * @author Sam Brannen * @since 3.2 */ public final class SpringFactoriesLoader { /** * The location to look for factories. *

    Can be present in multiple JAR files. */ public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class); private static final Map> cache = new ConcurrentReferenceHashMap<>(); private SpringFactoriesLoader() { } /** * Load and instantiate the factory implementations of the given type from * {@value #FACTORIES_RESOURCE_LOCATION}, using the given class loader. *

    The returned factories are sorted through {@link AnnotationAwareOrderComparator}. *

    If a custom instantiation strategy is required, use {@link #loadFactoryNames} * to obtain all registered factory names. * @param factoryClass the interface or abstract class representing the factory * @param classLoader the ClassLoader to use for loading (can be {@code null} to use the default) * @throws IllegalArgumentException if any factory implementation class cannot * be loaded or if an error occurs while instantiating any factory * @see #loadFactoryNames */ public static List loadFactories(Class factoryClass, @Nullable ClassLoader classLoader) { Assert.notNull(factoryClass, "'factoryClass' must not be null"); ClassLoader classLoaderToUse = classLoader; if (classLoaderToUse == null) { classLoaderToUse = SpringFactoriesLoader.class.getClassLoader(); } List factoryNames = loadFactoryNames(factoryClass, classLoaderToUse); if (logger.isTraceEnabled()) { logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames); } List result = new ArrayList<>(factoryNames.size()); for (String factoryName : factoryNames) { result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse)); } AnnotationAwareOrderComparator.sort(result); return result; } /** * Load the fully qualified class names of factory implementations of the * given type from {@value #FACTORIES_RESOURCE_LOCATION}, using the given * class loader. * @param factoryClass the interface or abstract class representing the factory * @param classLoader the ClassLoader to use for loading resources; can be * {@code null} to use the default * @throws IllegalArgumentException if an error occurs while loading factory names * @see #loadFactories */ public static List loadFactoryNames(Class factoryClass, @Nullable ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); } private static Map> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap result = cache.get(classLoader); if (result != null) { return result; } try { Enumeration urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); result = new LinkedMultiValueMap<>(); while (urls.hasMoreElements()) { URL url = urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry entry : properties.entrySet()) { String factoryClassName = ((String) entry.getKey()).trim(); for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) { result.add(factoryClassName, factoryName.trim()); } } } cache.put(classLoader, result); return result; } catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } } @SuppressWarnings("unchecked") private static T instantiateFactory(String instanceClassName, Class factoryClass, ClassLoader classLoader) { try { Class instanceClass = ClassUtils.forName(instanceClassName, classLoader); if (!factoryClass.isAssignableFrom(instanceClass)) { throw new IllegalArgumentException( "Class [" + instanceClassName + "] is not assignable to [" + factoryClass.getName() + "]"); } return (T) ReflectionUtils.accessibleConstructor(instanceClass).newInstance(); } catch (Throwable ex) { throw new IllegalArgumentException("Unable to instantiate factory class: " + factoryClass.getName(), ex); } } }

    //通过此处具体定义了spring factories配置文件的路径位于`META-INF/spring.factories`
    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
    
    Spring boot在启动的时候,默认会有三个spring.factories文件,分别位于
    • spring-boot
    • spring-actuator-autoconfigure
    • spring-beans
  3. spring.factories具体内容
    # Application Context Initializers
    org.springframework.context.ApplicationContextInitializer=\
    org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
    org.springframework.boot.context.ContextIdApplicationContextInitializer,\
    org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
    org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
    
    这一段配置是关于Application Context Initializers,对应的接口是org.springframework.context.ApplicationContextInitializer,下边为该接口对应的实现类。
    spring.factories的格式为:
    • Key 是接口全限定Class name;
    • Value 是key对应实现类的全限定Class name,用逗号分隔。
  4.  //创建对应实现类的实例
     createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
    
    private  List createSpringFactoriesInstances(Class type, Class[] parameterTypes,
            ClassLoader classLoader, Object[] args, Set names) {
        List instances = new ArrayList<>(names.size());
        //names就是刚才从SpringFactoriesLoader获取到的具体实现类的binary name
        for (String name : names) {
            try {
                //通过之前获取到的线程上下文类加载器去加载对应的binary name
                Class instanceClass = ClassUtils.forName(name, classLoader);
                Assert.isAssignable(type, instanceClass);
                Constructor constructor = instanceClass.getDeclaredConstructor(parameterTypes);
                T instance = (T) BeanUtils.instantiateClass(constructor, args);
                instances.add(instance);
            }
            catch (Throwable ex) {
                throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
            }
        }
        return instances;
    }
    

你可能感兴趣的:(Spring Boot factories机制)