上節(jié)我們了解了springboot的應(yīng)用的啟動(dòng)原理,其所有的核心都在run方法里,來看一段簡單的代碼:
@SpringBootApplication
public class AppApplication {
public static void main(String[] args) {
SpringApplication.run(AppApplication.class, args);
}
還是原來的味道,我們上節(jié)了解了run,接下來看看注解springBootApplication的原理過程
SpringBootApplication
我們可以發(fā)現(xiàn)該注解位于org.springframework.boot.autoconfigure包下,基本上我們的springboot應(yīng)用都會(huì)用到該注解,都說該注解是自動(dòng)裝配作用,到底是如何運(yùn)作的呢?其實(shí)我們不難發(fā)現(xiàn)該注解是一個(gè)組合注解,代碼如下:
//springBootApplication.java
@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 {
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
@AliasFor(annotation = EnableAutoConfiguration.class)
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
*/
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
/**
* Base packages to scan for annotated components. Use {@link #scanBasePackageClasses}
* for a type-safe alternative to String-based package names.
* @return base packages to scan
* @since 1.3.0
*/
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
/**
* Type-safe alternative to {@link #scanBasePackages} for specifying the packages to
* scan for annotated components. The package of each class specified will be scanned.
* <p>
* Consider creating a special no-op marker class or interface in each package that
* serves no purpose other than being referenced by this attribute.
* @return base packages to scan
* @since 1.3.0
*/
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
}
我們接下來看一下這些注解的作用:
Inherited
該注解是JDK自帶的注解,該注解一般作用于類上時(shí),表明子類是可以繼承它的,對(duì)方法或者屬性時(shí)無效的.關(guān)于該注解的詳解這里給大家推薦一篇寫的不錯(cuò)的文章關(guān)于java 注解中元注解Inherited的使用詳解,博主寫的很詳細(xì)這里就不多說了,我們接著看
SpringBootConfiguration
該注解位于org.springframework.boot.@SpringBootConfiguration包下,其主要的作用是用來標(biāo)記是一個(gè)springboot的配置類,我們來看代碼:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
可以看到的是,我們該注解的上面還是繼承spring的Configuration的注解,就是表明是一個(gè)配置類.
ComponentScan
該注解位于org.springframework.context.annotation包下,其主要的作用是用來掃描指定路徑下的組件(@Componment、@Configuration、@Service等標(biāo)注的注解)
EnableAutoConfiguration
該注解位于org.springframework.boot.autoconfigure包下,其主要的作用是用來開啟自動(dòng)裝配的功能,也是springboot項(xiàng)目中最核心的注解,我們來看代碼:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.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 {};
}
從代碼中看到,AutoConfigurationPackage注解引起了我們的關(guān)注,該注解位于org.springframework.boot.autoconfigure包下,其主要的作用是自動(dòng)配置包,可以獲取主程序所在的包路徑,同時(shí)將組件注冊到spring的容器中,代碼如下:
/**
* Indicates that the package containing the annotated class should be registered with
* {@link AutoConfigurationPackages}.
*
* @author Phillip Webb
* @since 1.3.0
* @see AutoConfigurationPackages
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
從代碼中可以看到,我們需要的注重點(diǎn)放在注解Import上,該注解位于org.springframework.context.annotation包下,其主要的作用是引入別的資源文件,關(guān)于該注解的使用可以看這篇文章@Import注解——導(dǎo)入資源,其中最重要的AutoConfigurationImportSelector才是我們需要重點(diǎn)分析的對(duì)象,接下來我們來看.
AutoConfigurationImportSelector
該類位于org.springframework.boot.autoconfigure包下,分別實(shí)現(xiàn)了DeferredImportSelector、BeanClassLoaderAware、ResourceLoaderAware、BeanFactoryAware、EnvironmentAware、Ordered 接口,來處理EnableAutoConfiguration注解的資源引入,來看代碼:
getCandidateConfigurations
該getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) 方法主要是用來獲取符合條件的配置類數(shù)組:
//AutoConfigurationImportSelector.java
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
//1.從META-INF/spring.factories文件下加載指定類型的類的數(shù)組
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;
}
- 在1. 處,通過調(diào)用#getSpringFactoriesLoaderFactoryClass()在meta-inf/spring.factories下加載我們的配置類EnableAutoConfiguration,來看代碼:
/**
* Return the class used by {@link SpringFactoriesLoader} to load configuration
* candidates.
* @return the factory class
*/
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
在來接著看,在上述代碼中通過SpringFactoriesLoader#loadFactoryNames(...)方法從meta-inf/spring.factories中加載指定類型EnableAutoConfiguration的類.來看一張圖:
我們來看通過#getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes)方法是如何調(diào)用的過程,來看張調(diào)用過程的圖:
在上圖的執(zhí)行過程中我們總結(jié)了大概有3步的過程,分別來看下:
- 在第1步處,通過調(diào)用#refresh來準(zhǔn)備spring容器的初始化過程,關(guān)于該部分的詳解我們在上篇[spring容器之從bean的實(shí)例中獲取對(duì)2(http://www.lxweimin.com/p/b587b501bec6)說的很清楚了,想了解的可以去看看 .
- 在第2步處,通過#getCandidateConfigurations(...)從meta-inf/spring.factories文件下加載所有的EnableAutoConfiguration配置類型數(shù)組
- 第3步,才是我們接下來需要了解的東西,首先我們來看一段代碼:
private final DeferredImportSelector.Group group;
public Iterable<Group.Entry> getImports() {
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
return this.group.selectImports();
}
}
該段代碼來源于org.springframework.context.annotation.ConfigurationClassParser下內(nèi)部類DeferredImportSelectorGroupingHandler#Iterable(...)方法,該方法的主要作用配置由注解import導(dǎo)入的每一個(gè)關(guān)聯(lián)的配置類.
- 在上述代碼中我們可以看到的是通過調(diào)用DeferredImportSelector.group#process(AnnotationMetadata metadata, DeferredImportSelector selector)方法來處理標(biāo)有注解import的注解.后面來說.
- 在方法的最后通過調(diào)用DeferredImportSelector.group.selectImports()來選擇需要導(dǎo)入的配置類.后續(xù)來說.
從上圖中我們可以看到在此處該方法導(dǎo)入了22個(gè)配置類.在我們這段代碼中一直有一個(gè)東東我們需要搞明白,那就是Group實(shí)例是如何來的問題,在我們的AutoConfiguationImportSelector類中有一個(gè)方法getImportGroup來獲取對(duì)應(yīng)的Group實(shí)例的接下來我們來看代碼:
getImportGroup
public Class<? extends Group> getImportGroup() {
return AutoConfigurationGroup.class;
}
該方法位于AutoConfiguationImportSelector,其次是該類實(shí)現(xiàn)了DeferredImportSelector接口,接著看:
AutoConfigurationGroup
首先是AutoConfigurationGroup位于AutoConfiguationImportSelector類的內(nèi)部類,同樣的該類實(shí)現(xiàn)了DeferredImportSelector.Group .BeanClassLoaderAware以及BeanFactoryAware和ResourceLoaderAware接口.
/**AnnotationMetadata類型的映射*/
private final Map<String, AnnotationMetadata> entries = new LinkedHashMap<>();
/**保存AutoConfigurationEntry的集合*/
private final List<AutoConfigurationEntry> autoConfigurationEntries = new ArrayList<>();
private ClassLoader beanClassLoader;
private BeanFactory beanFactory;
private ResourceLoader resourceLoader;
private AutoConfigurationMetadata autoConfigurationMetadata;
上面這些代碼是AutoConfigurationGroup類的屬性,在該類中有一個(gè)AutoConfigurationGroup#process(...)方法,在該方法中是進(jìn)行的賦值操作,包括我們的entries屬性,其中key為配置類的全限定名稱,來看張圖:
看完了上圖中entries屬性的賦值過程,發(fā)現(xiàn)也沒有我們想象的那么難,接著我們來看autoConfigurationEntries屬性的賦值過程,同樣是在#process(...)方法中進(jìn)行賦值的操作,來看:
我們也看到了,其中AutoConfigurationEntry為AutoConfigurationImportSelector的內(nèi)部類,來看代碼:
protected static class AutoConfigurationEntry {
/** 配置類的全類名的數(shù)組*/
private final List<String> configurations;
/***
* 排除的配置類的全類名的數(shù)組
*/
private final Set<String> exclusions;
private AutoConfigurationEntry() {
this.configurations = Collections.emptyList();
this.exclusions = Collections.emptySet();
}
接著看,其中AutoConfigurationMetadata屬性主要的作用是用來保存自動(dòng)配置的的元數(shù)據(jù).通過方法#getAutoConfigurationMetadata()來初始化的,來看代碼:
private AutoConfigurationMetadata getAutoConfigurationMetadata() {
//如果不存在的話,進(jìn)行加載
if (this.autoConfigurationMetadata == null) {
this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
}
//存在的話直接返回
return this.autoConfigurationMetadata;
}
簡單的來說一下,要想讓InfluxDbAutoConfiguration配置類生效,對(duì)應(yīng)的InfluxDbAutoConfiguration類上@ConditionalOnClass({ InfluxDB.class }) 注解上就應(yīng)該有對(duì)應(yīng)的類型,因此,所以,autoConfigurationMetadata 屬性,用途就是制定配置類(Configuration)的生效條件(Condition)
process方法
該方法位于#AutoConfigurationImportSelector#AutoConfigurationGroup內(nèi)部類的# process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector)方法:
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
//
Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
() -> String.format("Only %s implementations are supported, got %s",
AutoConfigurationImportSelector.class.getSimpleName(),
deferredImportSelector.getClass().getName()));
//1.獲取AutoConfigurationEntry實(shí)例
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
//2.保存
this.autoConfigurationEntries.add(autoConfigurationEntry);
//3.遍歷添加到entries中
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
該方法就做了上述代碼的三件事我們來看一下,其中我們的參數(shù)AnnotationMetadata一般是用來標(biāo)注注解SpringBootApplication的元數(shù)據(jù),因?yàn)槲覀兊淖⒔釹pringBootApplication組合了注解EnableAutoConfiguration.參數(shù)deferredImportSelector一般是用來@EnableAutoConfiguration定義的import注解標(biāo)注的類,也就是AutoConfigurationImportSelector對(duì)象.
- 在1處,通過調(diào)用#AutoConfigurationImportSelector#getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) 方法來獲取AutoConfigurationEntry對(duì)象,我們來看該方法:
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
//1.判斷是否開啟,沒有的話,直接返回一個(gè)空數(shù)組
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
//2.獲取原注解的屬性
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//3.獲取符合條件的配置類數(shù)組
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
//4.去除重復(fù)的配置類
configurations = removeDuplicates(configurations);
//5.獲取需要排除的配置類
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
//5.1.校驗(yàn)排除的配置類
checkExcludedClasses(configurations, exclusions);
//5.2.從配置類數(shù)組中移除掉排除的配置類
configurations.removeAll(exclusions);
//6.過濾不符合的配置類從configurations中
configurations = filter(configurations, autoConfigurationMetadata);
//7.觸發(fā)自動(dòng)配置類引入完成的事件
fireAutoConfigurationImportEvents(configurations, exclusions);
//8.創(chuàng)建AutoConfigurationEntry對(duì)象
return new AutoConfigurationEntry(configurations, exclusions);
}
簡單的來看一下上述方法都做了些什么過程:
- 在1.處,通過調(diào)用#isEnabled(annotationMetadata)來判斷是否開啟,未開啟的話,直接返回一個(gè)空數(shù)組,代碼如下:
//AutoConfigurationImportSelector.java
protected boolean isEnabled(AnnotationMetadata metadata) {
//判斷'spring.boot.EnableAutoConfiguration'是否配置,是否開啟自動(dòng)配置
//默認(rèn)情況下(未配置)
if (getClass() == AutoConfigurationImportSelector.class) {
return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true);
}
return true;
}
- 在2處,通過調(diào)用方法# getAttributes(annotationMetadata)來獲取原注解的屬性,代碼如下:
protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
//
String name = getAnnotationClass().getName();
//獲取屬性
AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));
Assert.notNull(attributes, () -> "No auto-configuration attributes found. Is " + metadata.getClassName()
+ " annotated with " + ClassUtils.getShortName(name) + "?");
return attributes;
}
上述代碼中首先是通過方法#getAnnotationClass().getName()獲取到的是@EnableAutoConfiguration,所以這里返回的注解屬性只能是exclude和excludeName這兩個(gè).
- 在3處通過方法#getCandidateConfigurations(annotationMetadata, attributes)獲取符合條件的配置類的數(shù)組.
- 在4處,通過方法#removeDuplicates(configurations)將獲取到的配置類進(jìn)行過濾處理,這里選擇需要的,代碼如下:
protected final <T> List<T> removeDuplicates(List<T> list) {
return new ArrayList<>(new LinkedHashSet<>(list));
}
- 在5處,通過調(diào)用#getExclusions(annotationMetadata, attributes)方法來獲取需要排除的配置類,代碼如下:
//AutoConfigurationImportSelector.java
protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
Set<String> excluded = new LinkedHashSet<>();
//注解屬性上的exclude
excluded.addAll(asList(attributes, "exclude"));
//添加注解上的excludeName屬性
excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
//添加配置文件上的屬性
excluded.addAll(getExcludeAutoConfigurationsProperty());
return excluded;
}
從上面的代碼來看獲取排除類的方式總共有三種,分別來看:
- 通過注解上的exclude屬性來排除的.
//AutoConfigurationImportSelector.java
protected final List<String> asList(AnnotationAttributes attributes, String name) {
String[] value = attributes.getStringArray(name);
return Arrays.asList((value != null) ? value : new String[0]);
}
- 第二種是通過注解上的excludeName屬性來排除的
- 方式三是通過配置文件上的屬性來排除的,這里一般是spring.autoconfigure.exclude配置
private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";
private List<String> getExcludeAutoConfigurationsProperty() {
if (getEnvironment() instanceof ConfigurableEnvironment) {
Binder binder = Binder.get(getEnvironment());
return binder.bind(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class).map(Arrays::asList)
.orElse(Collections.emptyList());
}
String[] excludes = getEnvironment().getProperty(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class);
return (excludes != null) ? Arrays.asList(excludes) : Collections.emptyList();
}
- 在5.1.處,通過方法#checkExcludedClasses(configurations, exclusions)對(duì)我們要排除的配置類進(jìn)行合法性的檢驗(yàn),代碼如下:
private void checkExcludedClasses(List<String> configurations, Set<String> exclusions) {
//用來保存需要檢驗(yàn)的配置類
List<String> invalidExcludes = new ArrayList<>(exclusions.size());
//從集合exclusions中遍歷處理
//如果該配置類在exclusions中(不在configurations中),進(jìn)行添加操作
for (String exclusion : exclusions) {
//classPath中存在該配置類
if (ClassUtils.isPresent(exclusion, getClass().getClassLoader()) && !configurations.contains(exclusion)) {
invalidExcludes.add(exclusion);
}
}
//invalidExcludes不為null,拋IllegalStateException異常
if (!invalidExcludes.isEmpty()) {
handleInvalidExcludes(invalidExcludes);
}
}
/**
* Handle any invalid excludes that have been specified.
* @param invalidExcludes the list of invalid excludes (will always have at least one
* element)
*/
protected void handleInvalidExcludes(List<String> invalidExcludes) {
StringBuilder message = new StringBuilder();
for (String exclude : invalidExcludes) {
message.append("\t- ").append(exclude).append(String.format("%n"));
}
throw new IllegalStateException(String.format(
"The following classes could not be excluded because they are" + " not auto-configuration classes:%n%s",
message));
}
從上面的代碼中可以看處,排除的過程是這樣的,因?yàn)槲覀兊呐渲妙愂谴嬖谟赾lassPath下的,而不存在與configurations中,所以這樣就可以直接排除了,接著看:
- 在5.2.處,從我們的配置類數(shù)組中configurations移除掉要排除的配置類
- 在6處,通過方法#filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata)從configurations過濾掉不符合的配置類,后續(xù)來說該方法
- 在7處,通過方法# fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions)來觸發(fā)自動(dòng)配置類完成的一些列事件,代碼如下:
//AutoConfigurationImportSelector.java
private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
//1.在meta-INF目錄下的spring.factories中加載指定類型為AutoConfigurationImportListener的所有類的數(shù)組
List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
//不為null
if (!listeners.isEmpty()) {
//2.通過我們的配置類數(shù)組和exclusions來構(gòu)建AutoConfigurationImportEvent對(duì)象
AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
//3.遍歷 AutoConfigurationImportListener監(jiān)聽器們逐個(gè)通知
for (AutoConfigurationImportListener listener : listeners) {
//3.1設(shè)置AutoConfigurationImportListener屬性
invokeAwareMethods(listener);
//3.2.進(jìn)行通知操作
listener.onAutoConfigurationImportEvent(event);
}
}
}
上述代碼中,不能理解,大致做了三件事,我們分別來看一下:
- 首先是通過#getAutoConfigurationImportListeners()方法在meta-INF目錄下的spring.factories文件中去加載指定類型(AutoConfigurationImportListener)的所有類的集合,我們通過dbug會(huì)發(fā)現(xiàn)如下圖:
在我們的集合中有一個(gè)類,接著看:
- 在上述條件成立的情況下,也就是當(dāng)我們的listeners不為null時(shí):
- 在2處創(chuàng)建一個(gè)AutoConfigurationImportEvent事件對(duì)象
- 在3處遍歷 AutoConfigurationImportListener監(jiān)聽器們逐個(gè)通知
- 在3.1.處,通過調(diào)用#invokeAwareMethods(Object instance)方法來設(shè)置AutoConfigurationImportListener的相關(guān)屬性,代碼如下:
private void invokeAwareMethods(Object instance) {
//是Aware類型的
if (instance instanceof Aware) {
if (instance instanceof BeanClassLoaderAware) {
((BeanClassLoaderAware) instance).setBeanClassLoader(this.beanClassLoader);
}
if (instance instanceof BeanFactoryAware) {
((BeanFactoryAware) instance).setBeanFactory(this.beanFactory);
}
if (instance instanceof EnvironmentAware) {
((EnvironmentAware) instance).setEnvironment(this.environment);
}
if (instance instanceof ResourceLoaderAware) {
((ResourceLoaderAware) instance).setResourceLoader(this.resourceLoader);
}
}
}
代碼簡單,這里就不說啥了,我們接著看:
- 在3.2.處,對(duì)我們的事件進(jìn)行通知處理操作,代碼如下:
//ConditionEvaluationReportAutoConfigurationImportListener.java
public void onAutoConfigurationImportEvent(AutoConfigurationImportEvent event) {
if (this.beanFactory != null) {
ConditionEvaluationReport report = ConditionEvaluationReport.get(this.beanFactory);
report.recordEvaluationCandidates(event.getCandidateConfigurations());
report.recordExclusions(event.getExclusions());
}
}
代碼簡單,就不啰嗦了,繼續(xù)回到我們的getAutoConfigurationEntry(...)方法
- 在8處,創(chuàng)建AutoConfigurationEntry對(duì)象
關(guān)于process方法我們基本上完了,我們來看一下注解AutoConfigurationPackage的作用
AutoConfigurationPackage
該注解位于org.springframework.boot.autoconfigure包下,官方是這樣解釋的:
Class for storing auto-configuration packages for reference later (e.g. by JPA entity scanner)
大致的意思是這樣的,如果使用該注解@AutoConfigurationPackage所在的包,會(huì)被注冊到spring IOC容器中的一個(gè)bean,我們通過獲取該bean就能獲取該bean所在的包,如:獲取JPA等.
接下來,我們AutoConfigurationPackage類下的一些重要的方法
Registrar
Registrar是AutoConfigurationPackage類的一個(gè)內(nèi)部類,可以看到的是它實(shí)現(xiàn)了ImportBeanDefinitionRegistrar和DeterminableImports接口,代碼如下:
/**
* {@link ImportBeanDefinitionRegistrar} to store the base package from the importing
* configuration.
*/
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImport(metadata).getPackageName());
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImport(metadata));
}
}
在上述代碼中,我們需要關(guān)注幾個(gè)點(diǎn)如:
- 在方法#registerBeanDefinitions(...)中,需要我們new PackageImport對(duì)象作為參數(shù)的傳遞,那么PackageImport是什么,我們來看代碼:
//AutoConfigurationPackages.java
/**
* Wrapper for a package import.
*/
private static final class PackageImport {
//包名
private final String packageName;
PackageImport(AnnotationMetadata metadata) {
this.packageName = ClassUtils.getPackageName(metadata.getClassName());
}
public String getPackageName() {
return this.packageName;
}
@Override
public boolean equals(Object obj) {
if (obj == null || getClass() != obj.getClass()) {
return false;
}
return this.packageName.equals(((PackageImport) obj).packageName);
}
@Override
public int hashCode() {
return this.packageName.hashCode();
}
@Override
public String toString() {
return "Package Import " + this.packageName;
}
}
我們發(fā)現(xiàn)PackageImport類為AutoConfigurationPackages的內(nèi)部類,其主要的作用是用來獲取包名.接著看
register方法
該方法主要的作用是注冊包名的bean到spring容器中,以備后續(xù)通過該bean直接獲取對(duì)應(yīng)的包名,代碼如下:
private static final String BEAN = AutoConfigurationPackages.class.getName();
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
//如果存在該bean
if (registry.containsBeanDefinition(BEAN)) {
//獲取該bean的定義
BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
//獲取構(gòu)造參數(shù)以及構(gòu)造函數(shù)等
ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
//修改其包名屬性
constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));
}
//不存在該bean,創(chuàng)建一個(gè)GenericBeanDefinition兵注冊
else {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(BasePackages.class);
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(BEAN, beanDefinition);
}
}
在上述注冊的BEAN的類型為BasePackages類型的,其中BasePackages為AutoConfigurationPackages的內(nèi)部類,來看代碼:
/**
* Holder for the base package (name may be null to indicate no scanning).
*/
static final class BasePackages {
private final List<String> packages;
private boolean loggedBasePackageInfo;
BasePackages(String... names) {
List<String> packages = new ArrayList<>();
for (String name : names) {
if (StringUtils.hasText(name)) {
packages.add(name);
}
}
this.packages = packages;
}
public List<String> get() {
if (!this.loggedBasePackageInfo) {
if (this.packages.isEmpty()) {
if (logger.isWarnEnabled()) {
logger.warn("@EnableAutoConfiguration was declared on a class "
+ "in the default package. Automatic @Repository and "
+ "@Entity scanning is not enabled.");
}
}
else {
if (logger.isDebugEnabled()) {
String packageNames = StringUtils.collectionToCommaDelimitedString(this.packages);
logger.debug("@EnableAutoConfiguration was declared on a class " + "in the package '"
+ packageNames + "'. Automatic @Repository and @Entity scanning is " + "enabled.");
}
}
this.loggedBasePackageInfo = true;
}
return this.packages;
}
}
這段代碼實(shí)質(zhì)就是對(duì)packages的封裝過程,無需多言,接著我們來看看注冊的過程
- 在注冊的過程中,如果存在BEAN僅僅是對(duì)包名屬性進(jìn)行修改,其中是通過方法addBasePackages(...)來操作,我們來看該方法:
private static String[] addBasePackages(ConstructorArgumentValues constructorArguments, String[] packageNames) {
//獲取存在的包名屬性的值
String[] existing = (String[]) constructorArguments.getIndexedArgumentValue(0, String[].class).getValue();
Set<String> merged = new LinkedHashSet<>();
//進(jìn)行覆蓋操作
merged.addAll(Arrays.asList(existing));
merged.addAll(Arrays.asList(packageNames));
return StringUtils.toStringArray(merged);
}
在我們的注冊方法中,如果不存在該 BEAN ,則創(chuàng)建一個(gè) Bean ,并進(jìn)行注冊.
- 首先是通過#has(BeanFactory beanFactory)來判斷是否在spring容器中存在該bean,代碼如下:
/**
* Determine if the auto-configuration base packages for the given bean factory are
* available.
* @param beanFactory the source bean factory
* @return true if there are auto-config packages available
*/
public static boolean has(BeanFactory beanFactory) {
return beanFactory.containsBean(BEAN) && !get(beanFactory).isEmpty();
}
- 接著是通過get方法來從容器中獲取我們的bean,代碼如下:
/**
* Return the auto-configuration base packages for the given bean factory.
* @param beanFactory the source bean factory
* @return a list of auto-configuration packages
* @throws IllegalStateException if auto-configuration is not enabled
*/
public static List<String> get(BeanFactory beanFactory) {
try {
return beanFactory.getBean(BEAN, BasePackages.class).get();
}
catch (NoSuchBeanDefinitionException ex) {
throw new IllegalStateException("Unable to retrieve @EnableAutoConfiguration base packages");
}
}
到這里關(guān)于springboot的自動(dòng)裝配的過程分析的差不多了....