SpringBoot自动配置原理

SpringBoot自动配置原理

SpringBoot在启动过程中,会识别到启动类 将其保存在 SpringApplicationmainApplicationClass 属性中,在 SpringApplication 构造方法中指定:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();		// 识别启动类
	}

	private Class<?> deduceMainApplicationClass() {
		try {
			StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
			for (StackTraceElement stackTraceElement : stackTrace) {
				if ("main".equals(stackTraceElement.getMethodName())) {
					return Class.forName(stackTraceElement.getClassName());
				}
			}
		}
		catch (ClassNotFoundException ex) {
			// Swallow and continue
		}
		return null;
	}

SpringBoot 自动配置有两种方式:

@import

SpringBoot 在启动过程中,会去收集被 @Import 注解的类 作为一个 sourceClass

image-20220112105413976

image-20220112103111373

if (candidate.isAssignable(ImportSelector.class)) {
    // Candidate class is an ImportSelector -> delegate to it to determine imports
    Class<?> candidateClass = candidate.loadClass();
    ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,this.environment, this.resourceLoader, this.registry);
    Predicate<String> selectorFilter = selector.getExclusionFilter();
    if (selectorFilter != null) {
        exclusionFilter = exclusionFilter.or(selectorFilter);
    }
    if (selector instanceof DeferredImportSelector) {
        this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
    }
    else {
        String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
        Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
        processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
    }
}

image-20220112122518004

就会将类加到 imports 中,为了以后实例化

image-20220112122901118

会在之后解析 xxxAutoConfiguration 中的 @bean ,解析到之后,加入到容器里面

	// Process individual @Bean methods
	Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
	for (MethodMetadata methodMetadata : beanMethods) {
		configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
	}

读取 spring.factories 配置文件

org.springframework.boot.autoconfigure.AutoConfigurationImportSelector 的 prosess() 中:

@Override
		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()));
			AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
					.getAutoConfigurationEntry(annotationMetadata);
			this.autoConfigurationEntries.add(autoConfigurationEntry);
			for (String importClassName : autoConfigurationEntry.getConfigurations()) {
				this.entries.putIfAbsent(importClassName, annotationMetadata);
			}
		}

getAutoConfigurationEntry()

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

在 prepareRefresh() 的 load() 方法中,将 启动类 注册到 AnnotatedBeanDefinitionReader 中,以便以后识别

private final AnnotatedBeanDefinitionReader annotatedReader;

private int load(Class<?> source) {
		if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
			// Any GroovyLoaders added in beans{} DSL can contribute beans here
			GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class);
			load(loader);
		}
		if (isEligible(source)) {
			this.annotatedReader.register(source);
			return 1;
		}
		return 0;
	}

getCandidateConfigurations() 中调用 SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,
getBeanClassLoader());

在这里获取 META-INF 目录下 spring.factories 文件中的指定 keyorg.springframework.boot.autoconfigure.EnableAutoConfiguration

对应的类的权限定名称。这些类会经过一系列的排除,过滤,最后加入到 IOC 容器中供我们使用

手写一个 starter

  • 创建一个 springboot 工程,加入依赖

    				<dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.3.7</version>
            </dependency>
    
  • 编写一个 template 类

    public class JalivvTemplate {
        public void say() {
            System.out.println("jalivv so cool");
        }
    }
    
  • 编写一个 AutoConfiguration 类,在该类中将 template 对象注入到 IOC 容器中。

    public class JalivvAutoConfiguration {
    
        @Bean
        JalivvTemplate jalivvTemplate() {
            return new JalivvTemplate();
        }
    }
    
  • 编写一个 Selector 类,去实现 ImportSelector 接口,重写接口中的方法

    public class MyImportSelector implements ImportSelector {
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            return new String[]{JalivvAutoConfiguration.class.getName()};
        }
    }
    
  • 编写一个注解,开启这个 starter

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Import(MyImportSelector.class)
    public @interface EnableJalivv {
    }
    
  • 引入到我们自己的项目中

            <dependency>
                <groupId>com.jalivv.spring.boot</groupId>
                <artifactId>jalivv-spring-boot-starter</artifactId>
                <version>1.0</version>
            </dependency>
    
  • 在启动类加上 @EnableJalivv ,就能从 IOC 容器中 取到 JalivvTemplate 这个对象

image-20220112130046058

RedisAutoconfiguration

redisAutoConfiguration 在 spring.factories 配置文件中已经配置,采用加载该配置文件的方式 加入IOC 容器

# Auto Configure	其他的类已经省略
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\

在 redisAutoConfiguration 中,就配置了 RedisTemplate

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean(name = "redisTemplate")
	public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
			throws UnknownHostException {
		RedisTemplate<Object, Object> template = new RedisTemplate<>();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

	@Bean
	@ConditionalOnMissingBean
	public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
			throws UnknownHostException {
		StringRedisTemplate template = new StringRedisTemplate();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

}
posted @ 2022-01-12 13:08  jalivv  阅读(483)  评论(0)    收藏  举报