Springboot 自动配置原理

Springboot的自动配置基于JavaSPI和服务发现机制,通过spring.factories文件加载配置类,生成BeanDefinition,实现框架的自动化配置。在启动过程中,@EnableAutoConfiguration注解触发配置加载,AutoConfigurationImportSelector处理并导入类路径中的配置类。
摘要由CSDN通过智能技术生成

Springboot的自动配置原理

Springboot的自动配置原理大致分为两个步骤:

  • 使用类似 Java SPI 机制获取自动配置类
  • 根据自动配置类生成对应的 bean

其中,第二步根据配置类生成 bean ,只是我们常见的配置,可能会多一些条件配置注解,所以不做重点内容。这里重要阐述第一步。具体如下:

1. Java SPI 机制

java SPI 是 Java 内置的服务发现机制。这种发现机制帮助我们发现并装配服务。这个自动发现并装配的过程使得我们仅通过更换依赖,即可更换服务提供者,而无需修改代码。下面是例子:

首先构建如下4个项目:

├─Client
├─Interface
├─Provider1
└─Provider2

其中 Client 是客户端,Interface 是实现和客户端共用的接口,Provider1/Provider2 是两种接口实现。具体内容如下:

  1. 共用接口如下

    public interface MyInterface
    {
        public void sayHi(String message);
    }
    
  2. 实现 Provider1 如下

    public class MyInterfaceImpl1 implements MyInterface
    {
        @Override
        public void sayHi(String mesg){
            System.out.println("Hello, I am " + mesg + " --by implement1");
        }
    }
    
  3. 在 Provider1 项目下 resources/META-INF/services 下新增文件 org.example.MyInterface,文件内容如下:(文件名即共用接口名称)

    org.example.MyInterfaceImpl1
    
  4. 同样类似实现 Provider2

  5. 在 Client 项目的 pom.xml 中引入项目 Provider1,并在使用如下实现

    public class App 
    {
        public static void main( String[] args )
        {
            ServiceLoader<MyInterface> mI = ServiceLoader.load(MyInterface.class);
            for(MyInterface tmp : mI){
                tmp.sayHi("MAIN");
            }
        }
    }
    
  6. 输出结果

    Hello, I am MAIN --by implement1
    // 若将 Client 的 pom.xml 中依赖改为 Provider2 则会输出如下
    Hello, I am MAIN --by implement2
    
  7. 这样既可让 Client 根据依赖获取实现,而无需显示引用依赖(自动发现并装配)

Springboot 的自动配置类使用类似的技术,它在每个 starter 包的每个 META-INF 目录下放一个 spring.factories 文件,文件内容如下:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
xx.xx.xx.XXConfiguration1,\
xx.xx.xx.XXConfiguration2

这个配置的效果等效于给容器 Import 这些配置类。这样容器就可以自动的对某某框架配置 bean 了。

2. Springboot 获取配置类过程

Springboot 的自动配置过程如下:

  1. 应用启动类

    @SpringBootApplication
    public class SpringBootAutoconfigImportAnnotationApplication {
    
    	public static void main(String[] args) {
    		SpringApplication.run(SpringBootAutoconfigImportAnnotationApplication.class, args);
    		System.out.println("Yes, It's running");
    	}
    
    }
    

    SpringApplication.run() 这一行,Springboot 应用会启动,它启动过程中会有一个 BeanPostProcessor 来处理配置中的注解,以此从配置中生成 BeanDefinition。

    这个 BeanPostProcessor 处理 @SpringBootApplication 从而触发配置加载。@SpringBootApplication 的定义如下

  2. @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) })
    public @interface SpringBootApplication {
    

    这里面跟自动配置相关关的就是 @EnableAutoConfiguration 它的定义如下

  3. @EnableAutoConfiguration 的定义如下

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

    这其中@Import(AutoConfigurationImportSelector.class) 即用来导入类路径中的自动配置类。

  4. 当处理到 AutoConfigurationImportSelector 时,它的处理流程如下:

    // 1. 获取自动配置,然后保存
    public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
    			Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
    					() -> String.format("Only %s implementations are supported, got %s",
    							AutoConfigurationImportSelector.class.getSimpleName(),
    							deferredImportSelector.getClass().getName()));
        // 1.1 这里调用它的 getAutoConfigurationEntry 方法获取自动配置
    			AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
    					.getAutoConfigurationEntry(annotationMetadata);
        // 1.2 这里保存
    			this.autoConfigurationEntries.add(autoConfigurationEntry);
    			for (String importClassName : autoConfigurationEntry.getConfigurations()) {
    				this.entries.putIfAbsent(importClassName, annotationMetadata);
    			}
    		}
    
    // 2. 获取自动配置 - 读取 spring.factories 并去重,过滤,等操作
    protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    		if (!isEnabled(annotationMetadata)) {
    			return EMPTY_ENTRY;
    		}
    		AnnotationAttributes attributes = getAttributes(annotationMetadata);
    		// 2.1 这里从前面第一节的 spring.factories 中获取配置类
        	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);
    	}
    
    // 3. 从 spring.factories 中获取配置类
    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        	// 3.1 就是这里,使用 SpringFactoriesLoader 来获取配置类信息
    		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;
    	}
    

    经过这么一套流程下面,Springboot 就可以获取类路径中的配置类,就可以根据依赖给对应框架生成bean,然后就实现了 Springboot 的自动配置。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值