首页 > 编程语言 > 详细

SpringBoot自动配置加载

时间:2021-05-04 23:12:03      阅读:22      评论:0      收藏:0      [点我收藏+]

SpringBoot自动配置加载

? 在使用SpringBoot时,我们只要导入好相关的jar,写好启动类,启动类上加入@SpringBootApplication注解,一个简单的SpringBoot项目就能够启动了。

? 这里记录一下SpringBoot是如何加载配置的。

  • @SpringBootApplication

    这个注解中包含以下注解

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @SpringBootConfiguration
    @EnableAutoConfiguration
    @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
    		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
    

    其中

    • @SpringBootConfiguration 表示当前类是一个配置类
    • @ComponentScan标明了扫描路径
    • @EnableAutoConfiguration 开启自动配置
  • @EnableAutoConfiguration

    这个注解包含以下注解

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @AutoConfigurationPackage
    @Import(AutoConfigurationImportSelector.class)
    

    其中

    • @AutoConfigurationPackage

      这个注解中

      @Import(AutoConfigurationPackages.Registrar.class)
      

      在AutoConfigurationPackages.Registrar类中,实现了按照包去加载所有的bean。

      /**
      	 * {@link ImportBeanDefinitionRegistrar} to store the base package from the importing
      	 * configuration.
      	 */
      	static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
      
      		@Override
      		public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
      			register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
      		}
      
      		@Override
      		public Set<Object> determineImports(AnnotationMetadata metadata) {
      			return Collections.singleton(new PackageImports(metadata));
      		}
      
      	}
      

      通过debug,可以看到,packageNames是当前启动器所在的包,所以使用了@EnableAutoConfiguration后,就能够加载自己定义的类中所有的bean对象。

    • @Import(AutoConfigurationImportSelector.class)

      导入的AutoConfigurationImportSelector类中,可以找到以下方法

      	@Override
      	public String[] selectImports(AnnotationMetadata annotationMetadata) {
      		if (!isEnabled(annotationMetadata)) {
      			return NO_IMPORTS;
      		}
      		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
      		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
      	}
      

      在这个方法中,去进行其它的jar包中的bean导入。

      进入getAutoConfigurationEntry中

      /**
      	 * Return the {@link AutoConfigurationEntry} based on the {@link AnnotationMetadata}
      	 * of the importing {@link Configuration @Configuration} class.
      	 * @param annotationMetadata the annotation metadata of the configuration class
      	 * @return the auto-configurations that should be imported
      	 */
      	protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
      		if (!isEnabled(annotationMetadata)) {
      			return EMPTY_ENTRY;
      		}
      		AnnotationAttributes attributes = getAttributes(annotationMetadata);
              //这里获取所有的自动配置
      		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
      		configurations = removeDuplicates(configurations);
      		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
      		checkExcludedClasses(configurations, exclusions);
      		configurations.removeAll(exclusions);
      		configurations = getConfigurationClassFilter().filter(configurations);
      		fireAutoConfigurationImportEvents(configurations, exclusions);
      		return new AutoConfigurationEntry(configurations, exclusions);
      	}
      
      List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
      

      这段代码获得了所有的自动配置configuration。

      /**
      	 * Return the auto-configuration class names that should be considered. By default
      	 * this method will load candidates using {@link SpringFactoriesLoader} with
      	 * {@link #getSpringFactoriesLoaderFactoryClass()}.
      	 * @param metadata the source metadata
      	 * @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
      	 * attributes}
      	 * @return a list of candidate configurations
      	 */
      	protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
      		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
      				getBeanClassLoader());
      		Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
      				+ "are using a custom packaging, make sure that file is correct.");
      		return configurations;
      	}
      

      通过上面代码,可以看到所有的配置都是通过SpringFactoriesLoader.loadFactoryNames得到的。进入方法,能够看到,它最终是这样去加载配置的。

      /**
      	 * Load the fully qualified class names of factory implementations of the
      	 * given type from {@value #FACTORIES_RESOURCE_LOCATION}, using the given
      	 * class loader.
      	 * <p>As of Spring Framework 5.3, if a particular implementation class name
      	 * is discovered more than once for the given factory type, duplicates will
      	 * be ignored.
      	 * @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<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
      		ClassLoader classLoaderToUse = classLoader;
      		if (classLoaderToUse == null) {
      			classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
      		}
      		String factoryTypeName = factoryType.getName();
              //++++++这里!!!
      		return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
      	}
      
      	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 {
                  //+++++这里!!!
                  //FACTORIES_RESOURCE_LOCATION这个在类中定义为"META-INF/spring.factories"
      			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;
      	}
      

      最终是通过

      Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
      

      来加载所有的配置。即,在jar包中,获取META-INF/spring.factories中的配置。

      我们可以在spring-boot-autoconfigure-2.4.5.jar中的META-INF/spring.factories里看到所有的auto configuration。

      到那时并非这个配置中的所有configuration都会被加载。

      在这个包中,能够看到有很多的加载类:

      AopAutoConfiguration.java,

      ApplicationAvailabilityAutoConfiguration.java,

      BasicBatchConfigurer.java

      ......

      进去会发现,它们都使用了@ConditionalOnBean(xxx.class),@ConditionalOnMissingBean(xxx.class)之类的条件注解,来确保运行正常。

      所以,最终在加载bean的过程中,会根据用户引入的jar包,注入所使用的所有bean。

SpringBoot自动配置加载

原文:https://www.cnblogs.com/reclusiveone/p/14730097.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!