SpringBoot自动配置原理分析

1. 从@SpringBootApplication注解入手

SpringBoot自动配置来源于@SpringBootApplication注解,该注解为复合注解!

@SpringBootApplication
public class HellowordApplication {
	public static void main(String[] args) {
		SpringApplication.run(HellowordApplication.class, args);
	}
}

@SpringBootApplication该注解下有
@SpringBootConfiguration:springboot的配置 、
@EnableAutoConfiguration:自动配置
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }):扫描当前主启动类同级包

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration  //springboot的配置 
@EnableAutoConfiguration  //自动配置
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

@SpringBootConfiguration注解

@SpringBootConfiguration注解下为@Configuration:spring的配置,@Configuration下为@Component:说明这也是一个spring的组件

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {

@EnableAutoConfiguration注解

@EnableAutoConfiguration注解下有@AutoConfigurationPackage@Import(AutoConfigurationImportSelector.class)
在这里插入图片描述

  1. @AutoConfigurationPackage:自动配置包
    1.1 @Import(AutoConfigurationImportSelector.class):导入选择器包注册
  2. @Import(AutoConfigurationImportSelector.class):自动配置导入选择,自动导入包的核心
    2.1 AutoConfigurationImportSelector:自动导入选择器,里面有环境、资源加载器等

AutoConfigurationImportSelector选择了什么东西

  • selectImports():选择组件,加载源数据,选择pom.xml配置的东西
  • getAutoConfigurationEntry():获取自动配置的实体
//获取候选的配置
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
  • 获取候选配置文件中配置
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;
	}

/**
	1.SpringFactoriesLoader.loadFactoryNames:获取所有的加载配置 详细见本文的spring工厂的加载
	2.getSpringFactoriesLoaderFactoryClass():标注了@EnableAutoConfiguration注解的类,就可以获取这下面的所有配置,就是获取主启动类加载的所有组件  EnableAutoConfiguration.class标注了这个类的所有包、所有配置
	3.getBeanClassLoader()→return this.beanClassLoader;当前类的加载器
	4.Assert.notEmpty:断言、非空,如果配置不为空就会找META-INF/spring.factories
**/

2. spring.factories文件

META-INF/spring.factories 自动配置的核心文件,里面有很多配置,如果没有的就需要手动配置,所有的自动配置类都在这里了

在这里插入图片描述

  • #Initializers 初始化的
  • #Application Listeners 监听的
  • #Auto Configuration Import Listeners 自动配置导入监听器
  • #Auto Configuration Import Filters 自动配置导入过滤器
  • #Auto Configure 自动配置,如果没有需手动导入

这么多自动配置为什么有的没有生效,需要导入对应的start才能有作用?
因为配置中有@Configuration(proxyBeanMethods = false)与@ConditionalOnXxx,@ConditionalOnXxx为核心注解,如果这里的条件都满足,才会生效

3.SpringFactoriesLoader:spring工厂的加载

//获取所有的加载配置,加载了一个类,即使标注了@SpringBootApplication的这样一个类
//作用就是从META-INF/spring.factories文件中读取指定类对应的类名称列表
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
        ClassLoader classLoaderToUse = classLoader;
        if (classLoader == null) {
            classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();//工厂的加载
        }

        String factoryTypeName = factoryType.getName();//获取类的名字
        return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
    }

/**
	枚举去遍历url,url从类加载器中获取所有的资源
	获取所有的系统资源,有一个while循环,这个url判断有没有更多的元素,如果有就把它放到url里,然后把url加载到properties里面,最后所有的东西都放到配置类里面去了,所有的资源都加载到配置类中
**/
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
        Map<String, List<String>> result = (Map)cache.get(classLoader);
        if (result != null) {
            return result;
        } else {
            HashMap result = new HashMap();

            try {
            
                Enumeration urls = classLoader.getResources("META-INF/spring.factories");
				//有一个while循环,这个url判断有没有更多的元素,如果有就把它放到url里
                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    //把url加载到properties里面
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();

                    while(var6.hasNext()) {
                        Entry<?, ?> entry = (Entry)var6.next();
                        String factoryTypeName = ((String)entry.getKey()).trim();
                        String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                        String[] var10 = factoryImplementationNames;
                        int var11 = factoryImplementationNames.length;

                        for(int var12 = 0; var12 < var11; ++var12) {
                            String factoryImplementationName = var10[var12];
                            ((List)result.computeIfAbsent(factoryTypeName, (key) -> {
                                return new ArrayList();
                            })).add(factoryImplementationName.trim());
                        }
                    }
                }

                result.replaceAll((factoryType, implementations) -> {
                    return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
                });
                cache.put(classLoader, result);
                return result;
            } catch (IOException var14) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
            }
        }
    }

4、总结

结论:springboot所有自动配置都是在启动的时候扫描并加载,所有的自动装配类都在spring.factories里面,但是不一定生效,@ConditionalOnXxx注解要判断是否条件成立,只要导入了对应的start,就有对应的启动器了,有了启动器我们自动装配就会生效

  1. springboot在启动的时候,从类路径下/META-INF/spring.factories获取@EnableAutoConfiguration指定的值;
  2. 将这些自动配置的类导入容器,自动配置就会生效,帮我们自动配置;
  3. 以前我们需要自动配置的东西,现在springboot帮我们做了
  4. 它会把所有需要导入的组件,以全类名的方式返回,这些组件就会被添加到容器
  5. 容器中也会存在非常多的xxxAutoConfiguration的文件(@Bean),就是这些类给容器中导入了这个场景需要的所有组件,并自动配置,@Configuration,JavaConfig
  6. 有了自动配置类,免去了我们手动编写配置文件的工作

精髓:

  • springboot启动会加载大量的自动配置类
  • 我们看我i们需要的功能有没有在SpringBoot默认写好的自动配置类当中
  • 再看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件存在在其中,我们就不需要再手动配置了)
  • 给容器中自动配置类添加组件的时候,会从properties类中获取某些属性,我们只需要在配置文件中指定这些属性的值即可
  • xxxxAutoConfiguration:自动配置类;给容器中添加组件
  • xxxxProperties:封装配置文件中相关属性

@Conditional指定的条件成立,才给容器中添加组件,配置里面的所有内容才会生效
@Conditional扩展注解

@ConditionalOnBean:仅仅在当前上下文中存在某个对象时,才会实例化一个Bean。
@ConditionalOnClass:某个class位于类路径上,才会实例化一个Bean。
@ConditionalOnExpression:当表达式为true的时候,才会实例化一个Bean。
@ConditionalOnMissingBean:仅仅在当前上下文中不存在某个对象时,才会实例化一个Bean。
@ConditionalOnMissingClass:某个class类路径上不存在的时候,才会实例化一个Bean。
@ConditionalOnNotWebApplication:不是web应用,才会实例化一个Bean。
@ConditionalOnBean:当容器中有指定Bean的条件下进行实例化。
@ConditionalOnMissingBean:当容器里没有指定Bean的条件下进行实例化。
@ConditionalOnClass:当classpath类路径下有指定类的条件下进行实例化。
@ConditionalOnMissingClass:当类路径下没有指定类的条件下进行实例化。
@ConditionalOnWebApplication:当项目是一个Web项目时进行实例化。
@ConditionalOnNotWebApplication:当项目不是一个Web项目时进行实例化。
@ConditionalOnProperty:当指定的属性有指定的值时进行实例化。
@ConditionalOnExpression:基于SpEL表达式的条件判断。
@ConditionalOnJava:当JVM版本为指定的版本范围时触发实例化。
@ConditionalOnResource:当类路径下有指定的资源时触发实例化。
@ConditionalOnJndi:在JNDI存在的条件下触发实例化。
@ConditionalOnSingleCandidate:当指定的Bean在容器中只有一个,或者有多个但是指定了首选的Bean时触发实例化。

可以通过启动debug=true属性,使控制台打印

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值