【SpringBoot】spring-boot-自动配置原理

思考:配置文件到底能写什么?怎么写?自动配置原理;

1.自动配置原理

springboot自动配置主演通过@EnableConfiguration,@Conditional,@EnableConfigurationProperties或者@ConfiguraionProperties几个注解来进行自动配置。@EnableConfiguraqtion开启自动配置,主要作用就是调用core包里的loadFactoryNames(),将autoconfig包里已经写好的自动配置加载进来。

@Conditional条件注解,通过判断类路径下有没有相应配置的jar包来确定是否加载和自动配置这个类,

@EnableConfigurationProperties的作用就是,给自动配置提供具体的配置参数,只需要写在application.proerties中,就可以通过映射写入配置类 的Pojo属性中。

1)SpringBoot启动的时候加载主配置类,开启自动配置功能@EnableAutoConfiguration

2)@EnableAutoConfiguration,利用EnableAutoConfigurationImportSelector给容器中导入一些组件。

3)从启动类的@SpringBootApplication进入,在里面找到@EnableAutoConfiguration


@SpringBootApplication
public class Application {

	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}
}
@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 {

4)EnableAutoConfigutation里通过@Import导入了EnableAutoConfigurationImportSelector

@SuppressWarnings("deprecation")
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

	/**
	 * Exclude specific auto-configuration classes such that they will never be applied.
	 * @return the classes to exclude
	 */
	Class<?>[] exclude() default {};

	/**
	 * Exclude specific auto-configuration class names such that they will never be
	 * applied.
	 * @return the class names to exclude
	 * @since 1.3.0
	 */
	String[] excludeName() default {};

}

5)进入EnableAutoConfigurationImportSelector的父类AutoConfigurationImportSelector,找到了selectImports()方法,调用了getCandidateConfigurations()方法,在这里又调用了Spring Core包中的loadFactoryNames()方法。这个方法的作用,扫描所有jar包类路径下 META-INF/spring.factories 把扫描到的这些文件的内容包装成properties对象,从properties中获取到EnableAutoConfig.class类(类名)对应的值,然后把他们添加到容器中

此方法找到getCandidateConfigurations()方法

	@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		try {
			AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
					.loadMetadata(this.beanClassLoader);
			AnnotationAttributes attributes = getAttributes(annotationMetadata);
			List<String> configurations = getCandidateConfigurations(annotationMetadata,
					attributes);
			configurations = removeDuplicates(configurations);
			configurations = sort(configurations, autoConfigurationMetadata);
			Set<String> exclusions = getExclusions(annotationMetadata, attributes);
			checkExcludedClasses(configurations, exclusions);
			configurations.removeAll(exclusions);
			configurations = filter(configurations, autoConfigurationMetadata);
			fireAutoConfigurationImportEvents(configurations, exclusions);
			return configurations.toArray(new String[configurations.size()]);
		}
		catch (IOException ex) {
			throw new IllegalStateException(ex);
		}
	}

 此方法找到loadFactoryNames()方法

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()方法,里面有一个常量FACTORIES_RESOURCE_LOCATION,他的值就是"META-INF/spring.factories";

public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
		String factoryClassName = factoryClass.getName();
		try {
			Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			List<String> result = new ArrayList<String>();
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
				String factoryClassNames = properties.getProperty(factoryClassName);
				result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
			}
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
					"] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}

6)jar文件在org.springframework.boot.autoconfigure的spring.factories

7)将类路径下META-INF/spring.factories里面配置的所有EnableAutoConfiguration的值加入到了容器中;

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\

每一个这样的xxxAutoConfiguration类都是容器中的一个组件,都加入到容器中;用他们来做自动配置;每一个自动配置类进行自动配置功能。

2.以HttpEncodingAutoConfiguration(http编码)为例解释自动配置

1)

@Configuration //表示这是一个配置类,以前编写的配置文件一样,也可以给容器中添加组件
@EnableConfigurationProperties(HttpEncodingProperties.class)//启动指
定类的ConfigurationProperties功能;将配置文件中对应的值和HttpEncodingProperties加入到ioc容器中
@ConditionalOnWebApplication//Spring底层@Conditional注解(spring注解版),根据不同的条件,
如果满足指定的条件,整个配置类里的配置就回生效;判断当前应用是否是web应用,如果是,当前配置类生效
@ConditionalOnClass(CharacterEncodingFilter.class)//判断当前项目有没有
这个类CharacterEncodingFilter,SpringMVC进行乱码解决的过滤器
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)//判断配置文件中是否存在某个配置 spring.http.encoding;如果不存在,
判断也是成立的,即使我们配置文件中不配置spring.http.encoding.enable=true,也是默认生效的
public class HttpEncodingAutoConfiguration {
    //他已经和SpringBoot的配置文件映射了
	private final HttpEncodingProperties properties;
   //
	public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) {
		this.properties = properties;
	}
   //给容器中添加一个组件,这些组件的值需要从properties中获取
	@Bean
	@ConditionalOnMissingBean(CharacterEncodingFilter.class)
	public CharacterEncodingFilter characterEncodingFilter() {
		CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
		filter.setEncoding(this.properties.getCharset().name());
		filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
		filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
		return filter;
	}

根据不同的条件判断,决定这个配置类是否生效?

一旦这个类配置生效;这个配置类就会给容器中添加各种组件;这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是配置文件绑定的;

2)所有在配置文件中能配置的属性都是在xxx.Properties类中封装着;配置文件能配置什么就可以参照某个功能对应的这个属性类

@ConfigurationProperties(prefix = "spring.http.encoding")//从配置文件中获取指定的值和      //bean的属性进行绑定
public class HttpEncodingProperties {

	public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
}

3.总结

1)SpringBoot启动会加载大量的自动配置类;

2)我们看我们需要的功能有没有SpringBoot默认写好的自动配置类;

3)我们再来看这个配置类到底配置了哪些组件;(只要我们用的组件有,我们就不需要再来配置了);

4)给容器中自动配置类添加组件的时候,会从 properties类中获取某些属性。我们就可以在配置文件中指定这些属性的值;

xxxConfiguration:自动配置类;给容器中添加组件

xxxProperties封装配置文件中相关属性

猜你喜欢

转载自blog.csdn.net/lbqlzch/article/details/82784036