Spring Boot自動配置(面試必備)

自動配置介紹

使用SpringBoot之後,一個整合了SpringMVC的WEB工程開發非常簡單,那些繁雜的配置都消失不見了,這是如何做到的?

查看main方法的啓動類

  • 註解:@SpringBootApplication
  • run方法:SpringApplication.run()
    在這裏插入圖片描述

查看@SpringBootApplication源碼

在這裏插入圖片描述

上面圖中有三個比較重要的註解

@SpringBootConfiguration

@SpringBootConfiguration的源碼

/**
 * Indicates that a class provides Spring Boot application
 * {@link Configuration @Configuration}. Can be used as an alternative to the Spring's
 * standard {@code @Configuration} annotation so that configuration can be found
 * automatically (for example in tests).
 * <p>
 * Application should only ever include <em>one</em> {@code @SpringBootConfiguration} and
 * most idiomatic Spring Boot applications will inherit it from
 * {@code @SpringBootApplication}.
  • @SpringBootConfiguration裏面又有一個@Configuration註解。

  • @Configuration註解:這個註解的作用就是聲明當前類是一個配置類,然後Spring會自動掃描到添加了@Configuration的類讀取其中的配置信息。而@SpringBootConfiguration是來聲明當前類是SpringBoot應用的配置類,項目中只能有一個。所以一般我們無需自己添加。

在這裏插入圖片描述

@EnableAutoConfiguration

SpringBoot源碼中,EnableAutoConfiguration.java 中這樣描述@EnableAutoConfiguration的功能和作用

/**
 * Enable(使能夠) auto-configuration of the Spring Application Context(環境,上下文), attempting(試圖,嘗試) to guess and
 * configure beans that you are likely to need. Auto-configuration classes are usually
 * applied(應用;實施) based on your classpath and what beans you have defined. For example, if you
 * have {@code tomcat-embedded.jar} on your classpath you are likely to want a
 * {@link TomcatServletWebServerFactory} (unless you have defined your own
 * {@link ServletWebServerFactory} bean).
 * 
 * <p>
 * When using {@link SpringBootApplication @SpringBootApplication}, the auto-configuration
 * of the context is automatically(自動地) enabled(啓動,激活) and adding this annotation has therefore no
 * additional(附加的) effect.
 * <p>
 * 
 * Auto-configuration tries(嘗試,試驗) to be as intelligent(智能的;聰明的) as possible and will back-away as you
 * define more of your own configuration. You can always manually(手動地) {@link #exclude(排除,排斥)()} any
 * configuration that you never want to apply (use {@link #excludeName()} if you don't
 * have access to them). You can also exclude them via the
 * {@code spring.autoconfigure.exclude} property. Auto-configuration is always applied
 * after user-defined beans have been registered.
 * <p>
 * The package of the class that is annotated with {@code @EnableAutoConfiguration},
 * usually via {@code @SpringBootApplication}, has specific(特殊的,特定的) significance(意義;重要性) and is often used
 * as a 'default'. For example, it will be used when scanning for {@code @Entity} classes.
 * It is generally recommended(推薦,介紹) that you place {@code @EnableAutoConfiguration} (if you're
 * not using {@code @SpringBootApplication}) in a root package so that all sub-packages(子包)
 * and classes can be searched(搜索,搜尋).
 * <p>
 * Auto-configuration classes are regular(定期的;有規律的) Spring {@link Configuration @Configuration}
 * beans. They are located using the {@link SpringFactoriesLoader} mechanism (keyed
 * against this class). Generally auto-configuration beans are
 * {@link Conditional @Conditional} beans (most often using
 * {@link ConditionalOnClass @ConditionalOnClass} and
 * {@link ConditionalOnMissingBean @ConditionalOnMissingBean} annotations).

總結@EnableAutoConfiguration的作用

  • 開啓自動配置
  • 基於我們所添加的jar包依賴,去“猜測”你想要如何配置Spring。比如我們引入了spring-boot-starter-web,而這個啓動器中幫我們添加了tomcat、SpringMVC的依賴,此時自動配置就知道你是要開發一個web應用,所以就幫你完成了web及SpringMVC的默認配置了!
  • SpringBoot內部對大量的第三方庫或Spring內部庫進行了默認配置,這些配置是否生效,取決於我們是否引入了對應庫所需的依賴。如果有那麼默認配置就會生效,我們使用SpringBoot構建一個項目,只需要引入所需框架的依賴,配置就可以交給SpringBoot處理。

@ComponentScan

@ComponentScan中的源碼的描述

/**
 * Configures component scanning directives for use with @{@link Configuration} classes.
 * Provides support parallel with Spring XML's {@code <context:component-scan>} element.
 *
 * <p>Either {@link #basePackageClasses} or {@link #basePackages} (or its alias
 * {@link #value}) may be specified to define specific packages to scan. If specific
 * packages are not defined, scanning will occur from the package of the
 * class that declares this annotation.
 *
 * <p>Note that the {@code <context:component-scan>} element has an
 * {@code annotation-config} attribute; however, this annotation does not. This is because
 * in almost all cases when using {@code @ComponentScan}, default annotation config
 * processing (e.g. processing {@code @Autowired} and friends) is assumed. Furthermore,
 * when using {@link AnnotationConfigApplicationContext}, annotation config processors are
 * always registered, meaning that any attempt to disable them at the
 * {@code @ComponentScan} level would be ignored.
 *
 * <p>See {@link Configuration @Configuration}'s Javadoc for usage examples.

@ComponentScan的作用和功能

  • 配置組件掃描的指令
  • 提供了類似與<context:component-scan>標籤的作用
  • 通過basePackageClasses或者basePackages屬性來指定要掃描的包。如果沒有指定這些屬性,那麼將從聲明這個註解的類所在的包開始,掃描包及子包。而我們的@SpringBootApplication註解聲明的類就是main函數所在的啓動類,因此掃描的包是該類所在包及其子包。所以,一般啓動類會放在一個比較前的包目錄中。

自動配置原理

Spring Boot啓動的時候會通過@EnableAutoConfiguration註解找到META-INF/spring.factories配置文件中的所有自動配置類,並對其進行加載,而這些自動配置類都是以AutoConfiguration結尾來命名的,它實際上就是一個JavaConfig形式的Spring容器配置類,它能通過以Properties結尾命名的類中取得在全局配置文件中配置的屬性如:server.port,而XxxxProperties類是通過@ConfigurationProperties註解與全局配置文件中對應的屬性進行綁定的。

源碼調試

@SpringBootApplication
public class StartApplication {

    public static void main(String[] args) {
    	//進入SpringBoot 啓動類的run方法
        SpringApplication.run(StartApplication.class, args);
    }

}
//進入run方法
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
		return run(new Class<?>[] { primarySource }, args);
	}
//進入SpringApplication類
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
		return new SpringApplication(primarySources).run(args);
	}
	//進入this,也就是SpringApplication類的構造方法
	public SpringApplication(Class<?>... primarySources) {
		this(null, primarySources);
	}
	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		
		//進入WebApplicationType.deduceFromClasspath()方法
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}
	//deduceFromClasspath() 方法根據依賴的jar包推測出配置類
	static WebApplicationType deduceFromClasspath() {
		if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
				&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
			return WebApplicationType.REACTIVE;
		}
		for (String className : SERVLET_INDICATOR_CLASSES) {
			if (!ClassUtils.isPresent(className, null)) {
				return WebApplicationType.NONE;
			}
		}
		return WebApplicationType.SERVLET;
	}

你知道的越多,你不知道的越多。
有道無術,術尚可求,有術無道,止於術。
如有其它問題,歡迎大家留言,我們一起討論,一起學習,一起進步

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章