springboot自動裝配原理探究 結論: SpringBoot啟動會載入大量的自動配置類 我們看我們需要的功能有沒有在SpringBoot預設寫好的自動配置類當中; 我們再來看這個自動配置類中到底配置了哪些組件;(只要我們要用的組件存在在其中,我們就不需要再手動配置了) 給容器中自動配置類添加組 ...
springboot自動裝配原理探究
結論:
- SpringBoot啟動會載入大量的自動配置類
- 我們看我們需要的功能有沒有在SpringBoot預設寫好的自動配置類當中;
- 我們再來看這個自動配置類中到底配置了哪些組件;(只要我們要用的組件存在在其中,我們就不需要再手動配置了)
- 給容器中自動配置類添加組件的時候,會從properties類中獲取某些屬性。我們只需要在配置文件中指定這些屬性的值即可;
對於配置類的讀取:
- 根據當前不同的條件判斷,決定這個配置類是否生效!
- 一但這個配置類生效;這個配置類就會給容器中添加各種組件;
- 這些組件的屬性是從對應的properties類中獲取的,這些類裡面的每一個屬性又是和配置文件綁定的;
- 所有在配置文件中能配置的屬性都是在xxxxProperties類中封裝著;
- 配置文件能配置什麼就可以參照某個功能對應的這個屬性類
- 首先創建一個springboot項目,打開主啟動類
存在如下註解:
-
@SpringBootApplication
放在項目的一個啟動類上,用來把啟動類註入到容器中,用來定義容器掃描的範圍,用來載入classpath環境中一些bean。
- 點進
@SpringBootApplication
可以看到包含如下註解:
-
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited
四大元註解。
-
@SpringBootConfiguration
標識一個springboot配置類
-
@EnableAutoConfiguration
啟動自動裝配
-
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
開啟組件掃描
- 先點進
@ComponentScan
中看看掃描了什麼組件
在這個註解下我們找到了這樣一個屬性:
/**
* Controls the class files eligible for component detection.
* <p>Consider use of {@link #includeFilters} and {@link #excludeFilters}
* for a more flexible approach.
*/
String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;
找到這個屬性值:
public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {
static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
}
可以看到幫我們導入了所有當前啟動類所在路徑下的任意類文件
- 接著點擊
@EnableAutoConfiguration
,讓我們去看看自動裝配都做了什麼
可以看到存在如下註解:
-
@AutoConfigurationPackage
自動配置包
-
@Import(AutoConfigurationImportSelector.class)
spring的底層註解,引入一個資源
- 點擊
@AutoConfigurationPackage
我們看到自動配置包引入了一個類資源,點進去看看
該類存儲來自導入配置的包
- 點擊
@Import(AutoConfigurationImportSelector.class)
,我們去看看這個註解引入的資源內容
這是一個自動配置導入選擇器
我們找到其中的個別方法
- 獲取自動配置實體
- 獲得候選配置
在候選配置中,我們註意到它調用了一個SpringFactoriesLoader.loadFactoryNames()
方法:
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
- 讓我們去看看
SpringFactoriesLoader
這個類
這是一個用於框架內部使用的通用工廠載入機制。
這是剛剛看到的loadFactoryNames()
方法:
這個類中還存在這樣一個方法:
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
Map<String, List<String>> result = cache.get(classLoader);
if (result != null) {
return result;
}
result = new HashMap<>();
try {
Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
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();
String[] factoryImplementationNames =
StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
for (String factoryImplementationName : factoryImplementationNames) {
result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
.add(factoryImplementationName.trim());
}
}
}
// Replace all lists with unmodifiable lists containing unique elements
result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
cache.put(classLoader, result);
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
return result;
}
在這個方法中我們可以看到,它載入了一個FACTORIES_RESOURCE_LOCATION
資源,並將從資源中拿到的東西封裝成一個個的Properties
供接下來使用
/**
* The location to look for factories.
* <p>Can be present in multiple JAR files.
*/
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
- 找到這個資源
META-INF/spring.factories
可以看到這個文件的內容就是我們用的一個又一個的配置
- 找個我們熟悉的,例如
HttpEncodingAutoConfiguration
點進去看看
看到瞭如下的註解:
@Configuration(proxyBeanMethods = false)
配置類標識
@EnableConfigurationProperties(ServerProperties.class)
啟用配置屬性
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
Spring底層@Conditional註解
根據不同的條件判斷,如果滿足指定的條件,整個配置類裡面的配置就會生效;
這裡的意思就是判斷當前應用是否是web應用,如果是,當前配置類生效@ConditionalOnClass(CharacterEncodingFilter.class)
判斷當前項目有沒有這個類CharacterEncodingFilter;SpringMVC中進行亂碼解決的過濾器;
@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)
判斷配置文件中是否存在某個配置:server.servlet.encoding.enabled;
如果不存在,判斷也是成立的
即使我們配置文件中不配置server.servlet.encoding.enabled=true,也是預設生效的;
可以看到它已經綁定了一個配置文件
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
//...
}
從此可以看到配置文件里寫的東西都在這個屬性類中封裝著,也就是說這個類里有的我們都可以在配置文件中配置
總結 :
-
根據當前不同的條件判斷,決定這個配置類是否生效!
-
一但這個配置類生效;這個配置類就會給容器中添加各種組件;
-
這些組件的屬性是從對應的properties類中獲取的,這些類裡面的每一個屬性又是和配置文件綁定的;
-
所有在配置文件中能配置的屬性都是在xxxxProperties類中封裝著;
-
配置文件能配置什麼就可以參照某個功能對應的這個屬性類
這就是自動裝配的原理!
xxxxAutoConfigurartion:自動配置類;給容器中添加組件
xxxxProperties:封裝配置文件中相關屬性;