在后面可以看到 getAutoConfigurationEntry()方法返回了一个对象 return new AutoConfigurationEntry(configurations, exclusions);这里也就是把我们需要的配置都拿到了。
那他是怎么拿到的候选的配置类呢? 我们接着看这个获取候选配置类的方法 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
进到方法后我们看到下面这个方法具体获取候选配置类的方法内容
这里我们跟着断点去走,首先进入getSpringFactoriesLoaderFactoryClass()方法
protected Class<?> getSpringFactoriesLoaderFactoryClass() { // 返回的是EnableAutoConfiguration字节码对象 return EnableAutoConfiguration.class; }接着我们在进入getBeanClassLoader()方法,这里就是一个类加载器
protected ClassLoader getBeanClassLoader() { return this.beanClassLoader; }最后我们在进入loadFactoryNames()方法,这个方法就是根据刚才的字节码文件和类加载器来找到候选的配置类。传递过来的字节码
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) { ClassLoader classLoaderToUse = classLoader; if (classLoaderToUse == null) { classLoaderToUse = SpringFactoriesLoader.class.getClassLoader(); } // 获取的EnableAutoConfiguration.class的权限定名 //org.springframework.boot.autoconfigure.EnableAutoConfiguration String factoryTypeName = factoryType.getName(); return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList()); }如下图:
最后通过loadSpringFactories()来获取到所有的配置类
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) { // 缓存加载的配置类 Map<String, List<String>> result = cache.get(classLoader); if (result != null) { return result; } result = new HashMap<>(); try { // 去资源目录下找 Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION); 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(); String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String) entry.getValue()); for (String factoryImplementationName : factoryImplementationNames) { result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>()) .add(factoryImplementationName.trim()); } } } // Replace all lists with unmodifiable lists containing unique elements result.replaceAll((factoryType, implementations) -> implementations.stream().distinct() .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList))); // 加载完成放到缓存中 cache.put(classLoader, result); } catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } // 返回加载到的配置类 return result; }这里我们要看下怎么从资源目录下 FACTORIES_RESOURCE_LOCATION 加载的。下面是加载配置文件的路径:
也就是项目启动的时候会去加载所有 META-INF 下的所有的 spring.factories 文件,我们搜一下这个这个文件,我搭建的是一个很简单的 SpringBoot 工程,它会去这几个 jar 里面找相关的配置类
但是最后自动装配的类是这个spring-boot-autoconfigure-2.4.3.RELEASE.jar
而根据EnabLeAutoConfiguration.class字节码加载的配置类就只有这118自动配置类
小结
实际上SpringBoot的自动装配原理,其实就是在项目启动的时候去加载META-INF下的 spring.factories 文件,好像也没有那么高大上。当然在启动的过程中还会有其他的配置项的加载,这里咱么直说了自动装配的加载过程。希望对大家可以有所启发。
问题∶ 明白了SpringBoot的自动装配原理, 如果我们需要让项目启动的时候就加载我们自定义的配置类, 该如何写呢?