(一)@SpringBootApplication源码详解

SpringBoot启动

Spring Boot 项目创建之后会默认生成一个入口类,通过该类的main方法即可启动Spring Boot项目:

@SpringBootApplication
public class StartApplication {
    public static void main(String args[]){
        SpringApplication.run(StartApplication.class,args);
    }
}

其中最重要的注解为@SpringBootApplication,以2.1.2版本为例:

@SpringBootApplication

@SpringBootApplication的源码如下,其中作为一个组合注解,包含了@SpringBootConfiguration、@EnableAutoConfiguration和@ComponentScan等关键注解。

package org.springframework.boot.autoconfigure;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AliasFor;

@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 {
    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    Class<?>[] exclude() default {};

    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    String[] excludeName() default {};

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackages"
    )
    String[] scanBasePackages() default {};

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackageClasses"
    )
    Class<?>[] scanBasePackageClasses() default {};
}

//更高版本的springboot中包含有proxyBwanMethods()

其中:

  • @AliasFor:用于桥接到其他注解,显示的覆盖元注解中的属性;相当于重新定义元注解属性,方便使用,详情可参考链接: Spring中的@AliasFor标签.
  • exclude():根据类排出指定的自动配置
  • excludeName():根据类名排除指定的自动配置
  • scanBasePackages():指定扫描的基础package
  • scanBasePackageClasses():扫描指定的类

通过这些属性,@SpringBootApplication组合了三个重要的注解,@SpringBootConfiguration、@EnableAutoConfiguration和@ComponentScan

@SpringBootConfiguration

@SpringBootConfiguration其中组合了@Configuration注解,二者作用一致

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}

@EnableAutoConfiguration

该注解用于实现Spring Boot的核心功能之一:约定优于配置,其主要功能是启动Spring应用程序上下文时进行自动配置,它会尝试配置项目可能需要的Bean,自动配置通常是基于项目classpath中引入的类和已定义的Bean实现的。在此过程中,被自动配置的组件来自项目本身和项目依赖的jar包中。

举个例子:如果将tomcat-embedded.jar添加到classpath下,那么@EnableAutoConfiguration类会为你准备使用TomcatServletWebServerFactory类,并帮你初始化相关配置。与此同时,如果自定义了基于ServletWebServerFactory的Bean,那么@EnableAutoConfiguration将不会进行TomcatServletWebServerFactory类的初始化。这一系列的操作判断都由SpringBoot来完成。(《Spring Boot 技术内幕》)
其源码为:

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;

@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 {};
}

其中:

  • ENABLED_OVERRIDE_PROPERTY:用来覆盖开启/关闭自动配置的功能
  • exclude()和excludeName()可以排除指定的配置;

而@EnableAutoConfiguration 的关键功能是由@Import({AutoConfigurationImportSelector.class})实现的

@Import

此处使用的@Import注解使用的是ImportSelector接口的方式,来将AutoConfigurationImportSelector引入,具体的@Import用法可以参考: @Import注解的作用.

AutoConfigurationImportSelector

AutoConfigurationImportSelector被调用的基本流程为:

  1. @Import注解启用该类
  2. selectImports方法被调用
  3. 检查自动配置是否开启
  4. 加载类路径下的metadata配置
  5. 加载spring.factories中的EnableAutoConfiguration配置类
  6. 配置类去重
  7. 获得被排除类集合
  8. 检查排除的类是否合法
  9. 配置类集合中去除被排除的类
  10. 过滤自动加载组件
  11. 将配置类和排除类通过事件传入监听器中
  12. 返回自动配置类全限定名数组

selectImports方法
selectImports方法基本包含了组件自动装配的所有处理逻辑,其源代码为:

public String[] selectImports(AnnotationMetadata annotationMetadata) {
    //检查自动配置是否开启
    if (!this.isEnabled(annotationMetadata)) {
        return NO_IMPORTS;
    } else {
        //加载类路径下的metadata配置
        AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
        AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }
}

protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
    if (!this.isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    } else {
        AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
        //获得自动加载配置
        List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
        
        //配置类去重
        configurations = this.removeDuplicates(configurations);
        
        //获得被排除类集合
        Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
        
        //检查排除的类是否合法
	this.checkExcludedClasses(configurations, exclusions);

	//配置类集合中去除被排除的类
        configurations.removeAll(exclusions);

	//过滤自动加载组件
        configurations = this.filter(configurations, autoConfigurationMetadata);

	//将配置类和排除类通过事件传入监听器中
        this.fireAutoConfigurationImportEvents(configurations, exclusions);

	//返回自动配置类全限定名数组
        return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
    }
}

检查自动配置是否开启
首先检查自动配置是否开启,如果自动配置功能开启,则继续执行,其判断的方式为:

protected boolean isEnabled(AnnotationMetadata metadata) {
    return this.getClass() == AutoConfigurationImportSelector.class ? (Boolean)this.getEnvironment().getProperty("spring.boot.enableautoconfiguration", Boolean.class, true) : true;
}

该方法获得配置文件中“spring.boot.enableautoconfiguration”的值,通过其判断是否开启自动配置;如果获取不到,则默认为true;可以通过在配置文件中修改spring.boot.enableautoconfiguration为false来关闭自动配置。

加载类路径下的metadata配置
加载元数据主要是为后续过滤自动配置使用

final class AutoConfigurationMetadataLoader {
    protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties";

    private AutoConfigurationMetadataLoader() {
    }

    public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
        return loadMetadata(classLoader, "META-INF/spring-autoconfigure-metadata.properties");
    }

    static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
        try {
            Enumeration<URL> urls = classLoader != null ? classLoader.getResources(path) : ClassLoader.getSystemResources(path);
            Properties properties = new Properties();

            while(urls.hasMoreElements()) {
                properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource((URL)urls.nextElement())));
            }

            return loadMetadata(properties);
        } catch (IOException var4) {
            throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", var4);
        }
    }

    static AutoConfigurationMetadata loadMetadata(Properties properties) {
        return new AutoConfigurationMetadataLoader.PropertiesAutoConfigurationMetadata(properties);
    }

    private static class PropertiesAutoConfigurationMetadata implements AutoConfigurationMetadata {
        ...
        }

    }
}

其中文件"META-INF/spring-autoconfigure-metadata.properties"中记录的是每一个需自动配置的类所需要的加载条件,后续步骤中只有满足该条件的自动配置类才会被加载,从而进行过滤。
该方法会将从文件中获取到的配置条件存储到Properties中,返回AutoConfigurationMetadata结构作为后续进行过滤的条件。

加载spring.factories中的EnableAutoConfiguration配置类
所需要加载的自动配置组件在类路径下的META-INF文件夹下的spring.factories文件中进行注册,在AutoConfigurationImportSelector中采用getCandidateConfigurations方法来获取需要配置的组件

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
}

protected Class<?> getSpringFactoriesLoaderFactoryClass() {
    return EnableAutoConfiguration.class;
}

可见该方法通过SpringFactoriesLoader类的loadFactoryNames方法获得全部自动加载配置,并返回List;其中SpringFactoriesLoader的实现方式为:

public final class SpringFactoriesLoader {
    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
    private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
    private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap();

    private SpringFactoriesLoader() {
    }

    public static <T> List<T> loadFactories(Class<T> factoryClass, @Nullable ClassLoader classLoader) {
        Assert.notNull(factoryClass, "'factoryClass' must not be null");
        ClassLoader classLoaderToUse = classLoader;
        if (classLoader == null) {
            classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
        }

        List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse);
        if (logger.isTraceEnabled()) {
            logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames);
        }

        List<T> result = new ArrayList(factoryNames.size());
        Iterator var5 = factoryNames.iterator();

        while(var5.hasNext()) {
            String factoryName = (String)var5.next();
            result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));
        }

        AnnotationAwareOrderComparator.sort(result);
        return result;
    }

    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 {
                Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                LinkedMultiValueMap result = new LinkedMultiValueMap();

                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;
            } catch (IOException var13) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
            }
        }
    }

    private static <T> T instantiateFactory(String instanceClassName, Class<T> factoryClass, ClassLoader classLoader) {
        try {
            Class<?> instanceClass = ClassUtils.forName(instanceClassName, classLoader);
            if (!factoryClass.isAssignableFrom(instanceClass)) {
                throw new IllegalArgumentException("Class [" + instanceClassName + "] is not assignable to [" + factoryClass.getName() + "]");
            } else {
                return ReflectionUtils.accessibleConstructor(instanceClass, new Class[0]).newInstance();
            }
        } catch (Throwable var4) {
            throw new IllegalArgumentException("Unable to instantiate factory class: " + factoryClass.getName(), var4);
        }
    }
}

其中:

  • FACTORIES_RESOURCE_LOCATION:加载文件路径
  • loadFactoryNames():获得全部类名
  • loadSpringFactories():加载全部META-INF/spring.factories文件,并按照相应的类名进行整合,得到全部配置列表

配置类去重

protected final <T> List<T> removeDuplicates(List<T> list) {
    return new ArrayList(new LinkedHashSet(list));
}

获得被排除类集合
根据上文可知,可以通过exclude和excludeName设置需要排除的配置;这里通过getExclusions()函数获得所有的需要排除的配置;

protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    Set<String> excluded = new LinkedHashSet();
    excluded.addAll(this.asList(attributes, "exclude"));
    excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
    excluded.addAll(this.getExcludeAutoConfigurationsProperty());
    return excluded;
}

private List<String> getExcludeAutoConfigurationsProperty() {
    if (this.getEnvironment() instanceof ConfigurableEnvironment) {
        Binder binder = Binder.get(this.getEnvironment());
        return (List)binder.bind("spring.autoconfigure.exclude", String[].class).map(Arrays::asList).orElse(Collections.emptyList());
    } else {
        String[] excludes = (String[])this.getEnvironment().getProperty("spring.autoconfigure.exclude", String[].class);
        return excludes != null ? Arrays.asList(excludes) : Collections.emptyList();
    }
}

除了exclude和excludeName属性设置,getExcludeAutoConfigurationsProperty()函数读取配置文件中的spring.autoconfigure.exclude配置,来获得需要排除的配置项;

检查排除的类是否合法
checkExcludedClasses()函数检查需要排除的类是否合法,主要检查内容为需要排除的类是否存在于从文件META-INF/spring.factories中;

private void checkExcludedClasses(List<String> configurations, Set<String> exclusions) {
    List<String> invalidExcludes = new ArrayList(exclusions.size());
    Iterator var4 = exclusions.iterator();

    while(var4.hasNext()) {
        String exclusion = (String)var4.next();
        if (ClassUtils.isPresent(exclusion, this.getClass().getClassLoader()) && !configurations.contains(exclusion)) {
            invalidExcludes.add(exclusion);
        }
    }

    if (!invalidExcludes.isEmpty()) {
        this.handleInvalidExcludes(invalidExcludes);
    }

}

protected void handleInvalidExcludes(List<String> invalidExcludes) {
    StringBuilder message = new StringBuilder();
    Iterator var3 = invalidExcludes.iterator();

    while(var3.hasNext()) {
        String exclude = (String)var3.next();
        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));
}

过滤自动加载组件
过滤部分主要使用的是AutoConfigurationImportFilter接口的相关实现
将配置类和排除类通过事件传入监听器中

private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
    List<AutoConfigurationImportListener> listeners = this.getAutoConfigurationImportListeners();
    if (!listeners.isEmpty()) {
        AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
        Iterator var5 = listeners.iterator();

        while(var5.hasNext()) {
            AutoConfigurationImportListener listener = (AutoConfigurationImportListener)var5.next();
            this.invokeAwareMethods(listener);
            listener.onAutoConfigurationImportEvent(event);
        }
    }

}

protected List<AutoConfigurationImportListener> getAutoConfigurationImportListeners() {
    return SpringFactoriesLoader.loadFactories(AutoConfigurationImportListener.class, this.beanClassLoader);
}

@ComponentScan

@ComponentScan默认会扫描该类所在的包下所有的配置类,并装配到容器中

参考

(《Spring Boot 技术内幕》)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值