SpringBoot 自动配置原理详解

自动配置类原理

一些公用或通用性的类或第三方的配置类,不需要每个项目都重复的编写,将他们抽取成自动配置类,使用的时候只需要引入即可;

代码实现:

public class A14 {

    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config",Config.class);
        //添加BeanFactory后处理器,解析注解
        context.registerBean(ConfigurationClassPostProcessor.class);
        context.refresh();
        for (String definitionName : context.getBeanDefinitionNames()) {
            System.out.println(definitionName);
        }
    }

    @Configuration //项目的配置,这里将第三方的配置类,引入到本项目中
    @Import({AutoConfiguration1.class, AutoConfiguration2.class})
    static class Config {

    }

    @Configuration //第三方的配置类
    static class AutoConfiguration1 {
        @Bean
        public Bean1 bean1() {
            return new Bean1();
        }
    }

    @Configuration //第三方的配置类
    static class AutoConfiguration2 {
        @Bean
        public Bean2 bean2() {
            return new Bean2();
        }
    }

    static class Bean1 {

    }

    static class Bean2 {

    }
}

优化:

我们在引入配置类的时候可以只引入一个类,该类帮我们去引入第三方配置类,代码如下:

@Configuration //项目的配置,这里将第三方的配置类,引入到本项目中
@Import({MyImportSelector.class})
static class Config {

}

static class MyImportSelector implements ImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{AutoConfiguration1.class.getName(), AutoConfiguration2.class.getName()};
    }
}

再次优化:

我们引入的第三方配置类名最好写在配置文件中,然后通过读取配置文件,引入第三方配置类,代码如下:

先在 resource 目录下新建一个 META-INF 目录,在该目录下新建一个 spring.factories 文件,在该文件中写入要引入的配置类全类名,

需要注意的是,如果配置类是内部类,需要用 $ 符。

com.wang.work.A14$MyImportSelector=\
com.wang.work.A14.AutoConfiguration1,\
com.wang.work.A14.AutoConfiguration2
@Configuration //项目的配置,这里将第三方的配置类,引入到本项目中
@Import({MyImportSelector.class})
static class Config {

}    

static class MyImportSelector implements ImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        List<String> names = SpringFactoriesLoader.loadFactoryNames(MyImportSelector.class, null);
        return names.toArray(new String[0]);
    }
}

注意:

  • 上述实现的 ImportSelector 接口,如果第三方的配置类和本项目的配置类重名,配置类会被覆盖,SpringBoot启动会先加载第三方的配置类,然后加载本项目的配置类,所以最后加载的是本项目的配置类;也可以关闭自动覆盖(SpringBoot 中默认是关闭的),不过有相同的配置类时会抛出异常。

  • SpringBoot启动会先加载第三方的配置类,然后加载本项目的配置类这样并不合理,加载顺序应该先加载本项目的,然后再加载第三方的才合理,这时,只需要将 ImportSelector 接口改为 DeferredImportSelector 接口即可;为了防止配置类冲突,在第三方配置类上加上 @ConditionalOnMissingBean 注解即可(SpringBoot 中就是这样做的),冲突之后就不会加载第三方的配置类。

        static class MyImportSelector implements DeferredImportSelector {
    
            @Override
            public String[] selectImports(AnnotationMetadata importingClassMetadata) {
                List<String> names = SpringFactoriesLoader.loadFactoryNames(MyImportSelector.class, null);
                return names.toArray(new String[0]);
            }
        }
    
        @Configuration //第三方的配置类
        static class AutoConfiguration1 {
            @Bean
            @ConditionalOnMissingBean
            public Bean1 bean1() {
                return new Bean1();
            }
        }
    

SpringBoot 自动配置源码分析:

  1. @SpringBootApplication 注解是一个复合注解,其内部 @EnableAutoConfiguration 注解就是开启自动配置
@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. @EnableAutoConfiguration 注解内部 @Import(AutoConfigurationImportSelector.class) 会通过加载AutoConfigurationImportSelector 来读取 spring.factories 配置文件里的配置类
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
  1. AutoConfigurationImportSelector 实现了 DeferredImportSelector,重写了 selectImports() 方法,getAutoConfigurationEntry() 方法内部会调用 getCandidateConfigurations() 方法,使用 spring.factories 文件加载器去加载 以AutoConfiguration 为键的所有配置类
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
            
    @Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}
       
    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);
	}
            
    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;
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot是一个简化Spring应用开发的框架,它通过自动配置的方式来减少开发者的工作量,提高开发效率。SpringBoot自动配置原理是什么呢? 首先,SpringBoot自动配置是基于SpringBoot的starter依赖功能实现的。starter依赖本质上是一个Maven项目,里面包含了一些依赖和必要的资源,只需将这个项目引入到应用中,就能自动加载依赖并进行一些默认配置Spring Boot框架中一些核心的starter依赖包括spring-boot-starter-web、spring-boot-starter-data-jpa、spring-boot-starter-data-redis等等,开发者可以根据自己的需要引入合适的starter依赖。 其次,SpringBoot自动配置是基于条件注解实现的。条件注解是Spring框架提供的一种特殊注解,它能够根据指定的条件来判断是否需要加载某个组件或配置Spring Boot中,提供了很多条件注解,如@ConditionalOnClass、@ConditionalOnProperty等等,它们可以根据是否存在、属性是否配置等条件来决定是否加载某个组件或配置。 最后,SpringBoot自动配置还包含了一些默认的配置,这些默认配置可以通过自定义配置来覆盖或扩展。例如,在使用spring-boot-starter-web时,SpringBoot会默认配置Tomcat容器来运行Web应用,如果开发者需要使用其他容器如Jetty或Undertow,可以写一个配置来覆盖默认配置。 综上所述,SpringBoot自动配置是基于starter依赖、条件注解和默认配置实现的。它大大减少了开发者的工作量,提高了开发效率,同时也保证了应用的可靠性和稳定性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值