1. 程式人生 > >【SpringBoot】spring-boot-自動配置原理

【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封裝配置檔案中相關屬性