springboot自动装配原理

springboot自动装配原理

  1. springboot的自动装配由注解@SpringBootApplication实现

    @SpringBootApplication:
        @SpringBootConfiguration
        @EnableAutoConfiguration
        @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
                @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
    

    此注解的实现如下,主要关注@SpringBootConfiguration,@EnableAutoConfiguration@ComponentScan三个注解

  2. @SpringBootConfiguration标注某一个类被当作一个springboot的配置类,可以看到它地层中有一个@configuration,将被作为一个组件加载到spring容器中

@SpringBootConfiguration:
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Configuration
    @Indexed
  1. @EnableAutoConfiguration主要用来实现自动配置功能
@EnableAutoConfiguration:
    @AutoConfigurationPackage
    @Import(AutoConfigurationImportSelector.class)

此处需要关注@AutoConfigurationPackage@Import(AutoConfigurationImportSelector.class)

  1. @AutoConfigurationPackage添加该注解的类所在的package 作为 自动配置package 进行管理,底层如下。

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @Import(AutoConfigurationPackages.Registrar.class)
    

    它会导入AutoConfigurationPackages.Registrar.class

    	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));
    		}
    
    	}
    

    这个方法会获取到启动类所在的包的包名,然后将其中的所有package设置为自动装配。

  2. AutoConfigurationImportSelector.class中加载了一些环境,并获取到了配置列表,此类为自动导入配置的核心

    AutoConfigurationImportSelector.class
    	List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    		
    		//此方法从META-INF/spring.factories中把所有的配置文件加载到容器中
    			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;
    	}
    

    loadFactoryNames()中会调用以下两个函数loadSpringFactories()loadSpringFactories()会遍历所有的配置文件,将它们的配置文件从FACTORIES_RESOURCE_LOCATION中加载资源,这个值就是先前的配置文件路径。

    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
    
    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 {
    			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;
    	}
    

getCandidateConfigurations()中,获取到的是EnableAutoConfiguration.class,即将标有@EnableAutoConfiguration注解的类所需要的配置文件自动装配。即上一步获取的所有配置文件都被装配了。

	protected Class<?> getSpringFactoriesLoaderFactoryClass() {
		return EnableAutoConfiguration.class;
	}

而在配置文件中存在存在@ConditionalOnXXX的注解,意思是只有满足某些条件时,这些配置文件才会生效。

总结:springboot 自动配置的核心是@EnableAutoConfiguration,这个注解的底层有两个地方需要注意,分别为@Import(AutoConfigurationImportSelector.class)@AutoConfigurationPackageAutoConfigurationImportSelector.class的作用是加载所有的配置文件,它会把所有的配置文件从META-INF/spring.factories路径下加载到一个properties文件中,并且为带有@EnableAutoConfiguration的类自动配置。而配置文件中有@ConditionalOnXXX类的注解,因此只有安装的starter,配置文件才会生效。

@AutoConfigurationPackage会首先获取到启动类所在包的包名,然后将启动类所在的package中所有的package作为自动配置package。这样所有的package中所有的需要配置的启动器都会被自动配置。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值