springboot源碼之自動(dòng)裝備原理

上節(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的類.來看一張圖:

微信截圖_20190906221139.png

我們來看通過#getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes)方法是如何調(diào)用的過程,來看張調(diào)用過程的圖:

微信截圖_20190906230558.png

在上圖的執(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ù)來說.
微信截圖_20190907095205.png

從上圖中我們可以看到在此處該方法導(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為配置類的全限定名稱,來看張圖:

微信截圖_20190907191127.png

看完了上圖中entries屬性的賦值過程,發(fā)現(xiàn)也沒有我們想象的那么難,接著我們來看autoConfigurationEntries屬性的賦值過程,同樣是在#process(...)方法中進(jìn)行賦值的操作,來看:

微信截圖_20190907174151.png

我們也看到了,其中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;
    }
微信截圖_20190907204621.png

簡單的來說一下,要想讓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)如下圖:
微信截圖_20190922092016.png

在我們的集合中有一個(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)裝配的過程分析的差不多了....

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,646評(píng)論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,595評(píng)論 3 418
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,560評(píng)論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,035評(píng)論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,814評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,224評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,301評(píng)論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,444評(píng)論 0 288
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,988評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,804評(píng)論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,998評(píng)論 1 370
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,544評(píng)論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,237評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,665評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,927評(píng)論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,706評(píng)論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,993評(píng)論 2 374

推薦閱讀更多精彩內(nèi)容