自動配置是springboot的一大特性,它能在咱們添加相應的jar包依賴的時候,自動爲咱們配置了這些組件的相關配置,咱們無需配置或者只須要少許的配置就能運行咱們編寫的項目。官網也對自動配置做了詳細說明:java
Spring Boot auto-configuration attempts to automatically configure your Spring application based on the jar dependencies that you have added. For example, if HSQLDB is on your classpath, and you have not manually configured any database connection beans, then Spring Boot auto-configures an in-memory database.web
上訴語句翻譯爲中文爲:SpringBoot自動配置嘗試根據您添加的JAR依賴性自動配置您的Spring應用程序。例如,若是HSQLDB在您的類路徑上,而且您沒有手動配置任何數據庫鏈接bean,那麼spring boot會自動配置內存中的數據庫。spring
若是須要設置自動配置的話,方法是將@EnableAutoconfiguration註解添加到您的@configuration類中。數據庫
@SpringBootApplication public class MMVirusScanApplication { public static void main(String[] args) { SpringApplication.run(MMVirusScanApplication.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 {}
這個註解最主要部分是:apache
<context:component-scan base-package="com.xxx.xxx"/>
, 組件掃描就是掃描指定的包下的類,並加載符合條件的組件。@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import({AutoConfigurationImportSelector.class}) public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; Class<?>[] exclude() default {}; String[] excludeName() default {}; }
能夠發現它是一個組合註解,Spring 中有不少以Enable開頭的註解,其做用就是藉助@Import來收集並註冊特定場景相關的bean,並加載到IoC容器。@EnableAutoConfiguration就是藉助@Import來收集全部符合自動配置條件的bean定義,並加載到IoC容器。裏面最主要註解是:springboot
@Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { //將註解標註的元信息傳入,獲取到相應的包名 register(registry, new PackageImport(metadata).getPackageName()); }
經過對registerBeanDefinitions方法進行DeBug,運行結果以下:併發
能夠看到AnnotationMetadata(註解標註注的元信息中包含了使用了哪些註解,相應的註解做用在哪一個類上)app
咱們對new PackageImport(metadata).getPackageName()進行檢索(idea工具能夠圈出須要查詢的值,使用快捷鍵「Ctrl+U」),看看其結果是什麼?框架
所以能夠得知使用@AutoConfigurationPackage註解就是將主程序類所在包及全部子包下的組件到掃描到spring容器中socket
@Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } //得到自動配置元信息,須要傳入beanClassLoader這個類加載器 AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry( autoConfigurationMetadata, annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); }
protected static final String PATH = "META-INF/" + "spring-autoconfigure-metadata.properties"; //文件中爲須要加載的配置類的類路徑 public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) { return loadMetadata(classLoader, PATH); } static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) { try { //讀取spring-boot-autoconfigure-2.1.5.RELEASE.jar包中spring-autoconfigure-metadata.properties的信息生成urls枚舉對象 Enumeration<URL> urls = (classLoader != null) ? classLoader.getResources(path) : ClassLoader.getSystemResources(path); Properties properties = new Properties(); //解析urls枚舉對象中的信息封裝成properties對象並加載 while (urls.hasMoreElements()) { properties.putAll(PropertiesLoaderUtils .loadProperties(new UrlResource(urls.nextElement()))); } //根據封裝好的properties對象生成AutoConfigurationMetadata對象返回 return loadMetadata(properties); } catch (IOException ex) { throw new IllegalArgumentException( "Unable to load @ConditionalOnClass location [" + path + "]", ex); } }
protected AutoConfigurationEntry getAutoConfigurationEntry( AutoConfigurationMetadata autoConfigurationMetadata, 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 = filter(configurations, autoConfigurationMetadata); fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); }
這個方法中有一個重要方法loadFactoryNames,這個方法是讓SpringFactoryLoader去加載一些組件的名字。
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { /** * 這個方法須要傳入兩個參數getSpringFactoriesLoaderFactoryClass()和getBeanClassLoader() * getSpringFactoriesLoaderFactoryClass()這個方法返回的是EnableAutoConfiguration.class * getBeanClassLoader()這個方法返回的是beanClassLoader(類加載器) */ 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; } /** * Return the class used by {@link SpringFactoriesLoader} to load configuration * candidates. * @return the factory class */ protected Class<?> getSpringFactoriesLoaderFactoryClass() { return EnableAutoConfiguration.class; } protected ClassLoader getBeanClassLoader() { return this.beanClassLoader; }
繼續點開loadFactory方法
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) { //獲取出入的鍵 String factoryClassName = factoryClass.getName(); return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); } private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader); if (result != null) { return result; } else { try { //若是類加載器不爲null,則加載類路徑下spring.factories文件,將其中設置的配置類的全路徑信息封裝 爲Enumeration類對象 Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories"); LinkedMultiValueMap result = new LinkedMultiValueMap(); //循環Enumeration類對象,根據相應的節點信息生成Properties對象,經過傳入的鍵獲取值,在將值切割爲一個個小的字符串轉化爲Array,方法result集合中 while(urls.hasMoreElements()) { URL url = (URL)urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); Iterator var6 = properties.entrySet().iterator(); while(var6.hasNext()) { Entry<?, ?> entry = (Entry)var6.next(); String factoryClassName = ((String)entry.getKey()).trim(); String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue()); int var10 = var9.length; for(int var11 = 0; var11 < var10; ++var11) { String factoryName = var9[var11]; result.add(factoryClassName, factoryName.trim()); } } } cache.put(classLoader, result); return result;
所以springboot底層實現自動配置的步驟是:
若是突發奇想恰好項目需求須要使用自動配置做一些操做,那麼接下來的環節將對你特別有幫助,精彩環節請不要離開。
1.一、我這裏以IntelliJ IDEA建立Project爲例,其實很簡單,建立一個Maven項目,可是注意建立的時候選擇quickstart,步驟以下:
1.二、點擊next,輸入GroupId和ArtifactId,繼續字母都爲小寫,通常以「com」開頭:
找到next繼續下一步。
1.三、配置好maven相關配置
1.四、輸入project name和設置好項目存放位置點擊finish便可。
建立完項目後在pom文件中引入spring-boot-autoconfigure 和httpclient的依賴,具體pom文件以下所示:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.demo</groupId> <artifactId>customproject</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <!--記住打包方式爲jar--> <name>customproject Maven Webapp</name> <!-- FIXME change it to the project's website --> <url>http://www.example.com</url> <properties> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> <maven.compiler.source>${java.version}</maven.compiler.source> <maven.compiler.target>${java.version}</maven.compiler.target> </properties> <dependencies> <!--引入httpclient依賴--> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.6</version> </dependency> <!--引入spring-boot-autoconfigure依賴--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> <version>2.1.5.RELEASE</version> </dependency> <!--lombok依賴 簡化實體類,封裝了set和get等方法--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.18</version> </dependency> </dependencies> <!--添加此配置能在maven使用install等命令時將resources目錄下文件都引入--> <build> <finalName>customproject</finalName> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> <resource> <directory>src/main/resources</directory> <includes> <include>**/*</include> </includes> </resource> </resources> </build> </project>
@Data @ConfigurationProperties(prefix = "spring.httpclient") class HttpClientProperties { private Integer connectTimeout = 1000;//建立鏈接的最長時間 private Integer socketTimeout = 10000;//數據傳輸的最長時間 private String agent = "agent"; private Integer maxPerRoute = 10;//設置每一個路由的併發數 private Integer maxTotal = 50;//最大鏈接數 } @ConfigurationProperties註解做用:讀取application.properties文件中的內容,根據配置的prefix屬性,將prefix屬性 對應的內容生成鍵值對複製給使用了@ConfigurationProperties註解類中
當咱們建立好配置信息類以後,使用@ConfigurationProperties註解是會出錯,出錯內容如圖所示:
緣由是咱們沒有使用到@EnableConfigurationProperties註解,當咱們使用了以後就不會出錯了。
@Configuration @ConditionalOnClass({HttpClient.class}) @EnableConfigurationProperties(HttpClientProperties.class) public class HttpClientAutoConfiguration { private HttpClientProperties httpClientProperties; public HttpClientAutoConfiguration(HttpClientProperties httpClientProperties) { this.httpClientProperties = httpClientProperties; } @Bean @ConditionalOnMissingBean(HttpClient.class) public HttpClient httpClient() { RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(httpClientProperties.getConnectTimeout()) .setSocketTimeout(httpClientProperties.getSocketTimeout()).build(); HttpClient httpClient = HttpClientBuilder.create().setDefaultRequestConfig(requestConfig) .setUserAgent(httpClientProperties.getAgent()).setMaxConnPerRoute(httpClientProperties.getMaxPerRoute()) .setConnectionReuseStrategy(new NoConnectionReuseStrategy()).build(); return httpClient; } } @Configuration:標註該類是一個配置類 @Bean:至關於原始的在xml文件中配置<bean id="">,聲明在方法上用於將實例對象注入Spring上下文中。 @ConditionalOnClass:該註解的參數對應的類必須存在,不然不解析該註解修飾的配置類; @ConditionalOnMissingBean:該註解表示,若是存在它修飾的類的bean,則不須要再建立這個bean;能夠給該註解傳入參數例如@ConditionOnMissingBean(name = 「example」),這個表示若是name爲「example」的bean存在,這該註解修飾的代碼塊不執行。 @EnableConfigurationProperties:會將會HttpClientProperties 做爲一個Bean引入HttpClientAutoConfiguration 中。
正常狀況下咱們按步驟一建立出來的Project是沒有resources這個文件夾的,在IntelliJ IDEA這個工具中,咱們須要先建立一個directory,而後將之設置爲resources root便可,設置方式以下:選中resources目錄右鍵單擊–》Mark Directory as–》Resource Root。
在resources目錄下新建META-INF目錄,而後在META-INF目錄下建立spring.factories文件,文件內容以下,表示設置自動配置類的位置,如有多個配置類用」,」隔開便可。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ cn.lijunkui.autoconfig.HttpClientAutoConfiguration #此處記得必定要配置HttpClientAutoConfiguration類所在包路徑
使用maven的install安裝到本地倉庫後,在建立好springboot以後,添加上依賴,就能夠很方便的使用了。