SpringBoot核心注解@SpringBootApplication和自动配置原理

@SpringBootApplication注解

@SpringBootApplication是一个3合一注解:

//@SpringBootApplication
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
public @interface SpringBootApplication {
    
}

@SpringBootConfiguration

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.boot;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}

@Configuration,代表当前是一个配置类

@ComponentScan

指定扫描哪些包,Spring注解。

@EnableAutoConfiguration

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

@AutoConfigurationPackage 导入主程序类所在的包及其子包

翻译过来叫自动配置包?指定了默认的包规则,看源码

@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {

它导入了一个Registrar,这是个内部类,点进去看源码,debug调试
image.png
利用Registrar给容器中导入一系列组件,将指定的一个包com.atguigu.boot下的所有组件导入进来

默认是主程序类MainApplication 所在包下。

image.png

@Import(AutoConfigurationImportSelector.class)导入spring.factories当中的配置类

利用getAutoConfigurationEntry(annotationMetadata);给容器中批量导入一些组件 
  1. 调用List configurations = getCandidateConfigurations(annotationMetadata,
    attributes)获取到所有需要导入到容器中的配置类 130个

  2. 利用工厂加载 Map<String, List> loadSpringFactories(@Nullable ClassLoader classLoader);得到所有的组件

  3. 从META-INF/spring.factories位置来加载一个文件。 默认扫描我们当前系统里面所有META-INF/spring.factories位置的文件,META-INF/spring.factories,位于spring-boot-autoconfigure-2.3.4.RELEASE.jar包当中META-INF/spring.factories

点进AutoConfigurationImportSelector去看源码selectImports方法

@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}

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

getCandidateConfigurations方法

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

loadFactoryNames方法

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
        String factoryTypeName = factoryType.getName();
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
    }

loadSpringFactories方法

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		try {
			Enumeration<URL> urls = (classLoader != null ?
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			result = new LinkedMultiValueMap<>();
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryTypeName = ((String) entry.getKey()).trim();
					for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
						result.add(factoryTypeName, factoryImplementationName.trim());
					}
				}
			}
			cache.put(classLoader, result);
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}

加载了一个配置文件叫"META-INF/spring.factories"
image.png

在这一步拿到所有的配置,返回上层我们看一共有130个

image.png

默认扫描我们当前系统里面所有spring.factories位置的文件,spring.factories位于spring-boot-autoconfigure-2.3.4.RELEASE.jar包当中META-INF/spring.factories,spring.factories文件里面写死了spring-boot一启动就要给容器中加载的所有配置类

image.png

spring.factories,这里当然也包括我们熟悉的伙伴spring-boot-starter-web的自动配置类包

image.png

总结以下,SpringBoot先加载所有的自动配置类 xxxxxAutoConfiguration

●每个自动配置类是个配置类@Configuration
●每个自动配置类按照条件进行生效@ConditionalOnXXXX
●生效的配置类就会给容器中装配很多组件@Bean,容器放了这些组件,相当于这些功能就有了
●更激动人心的是,自动配置类会为我们绑定参数配置文件,类注解@EnableConfigurationProperties(ServerProperties.class)

有趣的自动配置MultipartResolver

image.png

源码解读:

    @Bean
    //容器中有这个类型组件bean
    @ConditionalOnBean(MultipartResolver.class)  
    //容器中没有这个名字 multipartResolver 的组件bean
    @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) 
    //给@Bean标注的方法传入了对象参数,这个参数的值就会从容器中找。
    public MultipartResolver multipartResolver(MultipartResolver resolver) { 
      //SpringMVC multipartResolver。防止有些用户配置的文件上传解析器名字不叫multipartResolver,不符合规范
      // Detect if the user has created a MultipartResolver but named it incorrectly
      return resolver;
    }

给容器中加入了文件上传解析器;并且返回的resolver叫multipartResolver,因为我们的bean方法名叫multipartResolver
相当于强制的给用户配置的视图解析器重命名为multipartResolver

配置文件参数默认映射

@EnableConfigurationProperties

@EnableConfigurationProperties(ServerProperties.class)标注在类上

image.png

@ConfigurationProperties

@ConfigurationProperties(prefix = “server”)标注在参数类上

所以用户还可以去看这个组件是获取的yaml/properties配置文件什么值,prefix是什么,就去修改

@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)

public class ServerProperties {
	/**
	 * Server HTTP port.
	 */
	private Integer port;
	/**
	 * Network address to which the server should bind.
	 */
	private InetAddress address;
	@NestedConfigurationProperty
	private final ErrorProperties error = new ErrorProperties();
    ...
}

image.png

允许用户可以定制化组件

用户可以直接自己@Bean替换底层Spring默认的组件

有趣的组件CharacterEncodingFilter

image.png

底层的ConditionalOnMissingBean意味着Spring的一种设计模式:条件装载,当此Bean,CharacterEncodingFilter不存在时才生效,意味着会向容器当中放置一个Spring默认的CharacterEncodingFilter。SpringBoot默认会在底层配好所有的组件。但是如果用户自己配置了以用户的优先

@Bean
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
	CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
	filter.setEncoding(this.properties.getCharset().name());	         filter.setForceRequestEncoding(this.properties.shouldForce(Encoding.Type.REQUEST));	filter.setForceResponseEncoding(this.properties.shouldForce(Encoding.Type.RESPONSE));
	return filter;

}
  • 32
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值