聊聊springboot starter

前言:我们在创建一个springboot工程的时候,经常会依赖很多starter包,比如: [spring-boot-starter-actuator]、[spring-boot-starter-data-redis]、[spring-boot-starter-jdbc]等等。那么大家有没有想过别人开发的工程,我们通过自动注入的方式怎么会拿到他的对象?SpringBoot又是什么时候把这些对象以代理对象的形式存放进spring容器中的呢?下边我们就来聊聊如何创建一个我们自己的starter工程,并让其他工程通过maven依赖进来使用。

1.spring.factories是什么

如果我们打开一个starter工程的autoconfigure模块,会在他的类路径(classpath)的
META-INF目录下看到一个 spring.factories文件

spring.factories

这个文件其实就是给springboot去识别哪些类需要注入spring容器管理的,文件格式为

#key=value,key:spring特征类路径,value:我们的特征类路径
#比如需要创建一个自动配置的类MyAutoConfiguration.class
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.la.autoconfiguration.MyAutoConfiguration

#如果有多个类,使用","隔开,使用"\"进行换行,例如:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
xxx.A,\
xxx.B,\
xxx.C

常见的类型有以下几种:

  • Initializers
    org.springframework.context.ApplicationContextInitializer=
  • Application Listeners
    org.springframework.context.ApplicationListener=
  • Auto Configuration Import Listeners
    org.springframework.boot.autoconfigure.AutoConfigurationImportListener=
  • Auto Configuration Import Filters
    org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=
  • Auto Configure()
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=

2.spring.factories什么时候被加载

springboot在启动后,会遍历所有jar的类路径的META-INF目录,寻找spring.factories文件并解析。具体过程如下:
执行SpringApplication.run(Class primarySource, String... args)方法中调用了SpringFactoriesLoader去加载并解析spring.factories文件

/**
     * Load the fully qualified class names of factory implementations of the
     * given type from {@value #FACTORIES_RESOURCE_LOCATION}, using the given
     * class loader.
     * @param factoryType 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 factoryType, @Nullable ClassLoader classLoader) {
        String factoryTypeName = factoryType.getName();
        return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
    }

    private static Map> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap result = cache.get(classLoader);
        if (result != null) {
            return result;
        }

        try {
//---- 这里的FACTORIES_RESOURCE_LOCATION就是是字符串常量"META-INF/spring.factories" ----
            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 factoryTypeName = ((String) entry.getKey()).trim();
                    for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                        result.add(factoryTypeName, factoryImplementationName.trim());
                    }
                }
            }
            cache.put(classLoader, result);
            return result;
        }
        catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load factories from location [" +
                    FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
    }

SpringBoot启动后查找spring.factories文件的调用栈如下图:


获取spring.factories调用顺序.png

3.作用

简化第三方组件的配置使用,只要通过maven引入相关的依赖,springboot启动后就能自动找到并使用该组件的功能。

4.示例代码

https://github.com/M-u-94/spring-boot-study

你可能感兴趣的:(聊聊springboot starter)