• 浅看SpringBoot的自动装配


    系列文章目录

    SpringBoot启动的时候做了什么(1)
    SpringBoot启动的时候做了什么(2)



    前言

    SpringBoot的自动配置是一个很重要的特点,它可以大量的减少我们对相关框架的配置工作。之前两篇文章讲了SpringBoot的main方法内部逻辑,接下来开始学习SpringBoot的自动配置。


    正文

    @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) })
    public @interface SpringBootApplication {
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    @SpringBootApplication注解是个组合注解,其上有三个关键注解:

    • @SpringBootConfiguration
    • @EnableAutoConfiguration
    • @ComponentScan

    @SpringBootConfiguration 其实就是被Spring的@Configuration修饰的,两者功能一致,只是在SpringBoot中换了个名字而已。

    1:@EnableAutoConfiguration

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @AutoConfigurationPackage
    @Import(AutoConfigurationImportSelector.class)
    public @interface EnableAutoConfiguration {
    	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    	Class<?>[] exclude() default {};
    	String[] excludeName() default {};
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    1.1:@AutoConfigurationPackage

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @Import(AutoConfigurationPackages.Registrar.class)
    public @interface AutoConfigurationPackage {
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    可以看到,要分析@AutoConfigurationPackage 的作用,关键入口是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));
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    RegistrarAutoConfigurationPackages的一个静态内部类,再看register方法:

    private static final String BEAN = AutoConfigurationPackages.class.getName();
    public static void register(BeanDefinitionRegistry registry, String... packageNames) {
    	//BEAN 就是当前类,项目启动后第一次解析,肯定不会进第一个if分支
    	if (registry.containsBeanDefinition(BEAN)) {
    		BasePackagesBeanDefinition beanDefinition = (BasePackagesBeanDefinition) registry.getBeanDefinition(BEAN);
    		beanDefinition.addBasePackages(packageNames);
    	}
    	else {
    		registry.registerBeanDefinition(BEAN, new BasePackagesBeanDefinition(packageNames));
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    BEAN 就是当前类,项目启动后第一次解析,肯定不会进第一个if分支。
    第二个分支里边的逻辑就很简单了,就是注册一个BeanDefinition
    关于register方法的第一个参数BeanDefinitionRegistry之前写过文章,点我。⬅️👀
    回到调用register的地方🔙🔙🔙

    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    	register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
    }
    
    • 1
    • 2
    • 3
    • 4

    那接下来其实我们只要搞清楚register的第二个参数,就可以明白AutoConfigurationPackage做了什么。
    别闹了,不会还要看代码吧,直接debug不来的痛快么😂
    在这里插入图片描述
    debug结果可以看到new PackageImports(metadata).getPackageNames()其实就是获取本项目启动类所在的项目路径。
    在这里插入图片描述
    beanDefinitionMap中也看到了当前启动类项目路径已被注册。

    总结
    @AutoConfigurationPackage作用是将启动类所在路径注册到Spring。

    1.2:@Import(AutoConfigurationImportSelector.class)

    这里需要知道@Import是做什么的,此注解不做展开。

    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
    	if (!isEnabled(annotationMetadata)) {
    		return NO_IMPORTS;
    	}
    	AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
    	return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    !isEnabled(annotationMetadata):判断当前项目是否开启自动装配,是通过一个配置实现的,默认打开。很简单就贴代码了。
    重点看getAutoConfigurationEntry(annotationMetadata)

    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    1.2.1:getAttributes(annotationMetadata)

    @EnableAutoConfiguration有两个属性:

    Class<?>[] exclude() default {};
    String[] excludeName() default {};
    
    • 1
    • 2

    getAttributes(annotationMetadata)这个方法的作用就是获取这两个属性的值。

    1.2.2:getCandidateConfigurations(annotationMetadata, attributes)

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    	List<String> configurations = new ArrayList<>(
    			SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()));
    	ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);
    	Assert.notEmpty(configurations,
    			"No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you "
    					+ "are using a custom packaging, make sure that file is correct.");
    	return configurations;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    先看SpringFactoriesLoader.loadFactoryNames
    在这里插入图片描述
    在这里插入图片描述
    可以看到这里就是从META-INF/spring.factories中获取所有的EnableAutoConfiguration实现。

    再看ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);
    在这里插入图片描述
    这个方法就是从spring-boot-autoconfigure中获取springboot目前支持的各框架的配置类。
    简单看几个:
    在这里插入图片描述
    后边的就没啥必要看了,就是做一下去重、剔除之前exclued配置的。

    2:@ComponentScan

    这个下篇再说吧,要下班了


    总结

    META-INF/spring.factories中获取所有配置类,中间用到了SPI机制。

  • 相关阅读:
    form组件的封装(element ui ) 简单版本
    机器人抓取检测技术的研究现状
    微服务从代码到k8s部署应有尽有系列(五、民宿服务)
    智慧城市中的公共服务创新:让城市生活更便捷
    React-Context实现水印功能
    OpenCV入门8:区域分割和区域生长
    我的服务器被黑客攻击了!!
    [附源码]Python计算机毕业设计大学生社团管理系统
    C++11的学习
    pip install rosbag最正确方法网上其他都不对
  • 原文地址:https://blog.csdn.net/Paranoia_ZK/article/details/126039050