栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

SpringBoot启动过程分析——@SpringBootApplication注解分析

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

SpringBoot启动过程分析——@SpringBootApplication注解分析

启动类

调用SpringApplication.run方法启动,启动类使用@SpringBootApplication注解

@SpringBootApplication
public class StringBootDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(StringBootDemoApplication.class, args);
    }
}
@SpringBootApplication注解
@Target(ElementType.TYPE) // 注解的适用范围,其中TYPE用于描述类、接口(包括包注解类型)或enum声明
@Retention(RetentionPolicy.RUNTIME) // 注解的生命周期,保留到class文件中(三个生命周期中的运行时期)
@documented // 表明这个注解应该被javadoc记录
@Inherited // 子类可以继承该注解
@SpringBootConfiguration // 继承了Configuration,表示当前是注解类
@EnableAutoConfiguration // 开启springboot的注解功能,springboot的四大神器之一,其借助@import的帮助
@ComponentScan(excludeFilters = { // 扫描路径设置
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
...
} 

@Configuration注解

不同于原来springmvc中用xml配置文件的形式,在springboot中我们大多用配置类来解决配置问题,能够被@Configuration注解加载的bean:

1. xml配置文件的形式配置bean




2. java configuration的配置形式配置bean
@Configuration
public class MockConfiguration{
    //bean定义
}

任何一个标注了@Bean的方法,其返回值将作为一个bean定义注册到Spring的IoC容器,方法名将默认成该bean定义的id。

@ComponentScan注解

ComponentScan的功能其实就是自动扫描并加载符合条件的组件(比如@Component和@Repository等以及标签标注的xml文件里的bean)或者bean定义,将这些bean定义加载到IoC容器中
可以通过basePackages等属性来细粒度地定制@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架实现会从声明@ComponentScan所在类的package进行扫描。
所以SpringBoot的启动类最好是放在root package下,因为默认可以不指定basePackages


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

}

这个注解上还使用了两个注解:

  • @AutoConfigurationPackage
  • @import(AutoConfigurationimportSelector.class)

@AutoConfigurationPackage和@import(AutoConfigurationimportSelector.class)都使用了@import注解

@AutoConfigurationPackage

源代码:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@documented
@Inherited
@import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {

	
	String[] basePackages() default {};

	
	Class[] basePackageClasses() default {};

}

通过这个注解的源代码可以看出,这个注解又使用了@import(AutoConfigurationPackages.Registrar.class)注解,打开AutoConfigurationPackages.Registrar

static class Registrar implements importBeanDefinitionRegistrar, Determinableimports {

    //注册org.springframework.boot.autoconfigure.AutoConfigurationPackages的BeanDefinition
    //实际上就是注册当前启动类的根package
    @Override
    public void registerBeanDefinitions(Annotationmetadata metadata, BeanDefinitionRegistry registry) {
        register(registry, new Packageimports(metadata).getPackageNames().toArray(new String[0]));
    }

    @Override
    public Set determineimports(Annotationmetadata metadata) {
        return Collections.singleton(new Packageimports(metadata));
    }
    
}
 
import(AutoConfigurationimportSelector.class) 

1)AutoConfigurationimportSelector类
AutoConfigurationimportSelector 实现了 DeferredimportSelector 从 importSelector继承的方法:selectimports。

@Override
public String[] selectimports(Annotationmetadata annotationmetadata) {
    if (!isEnabled(annotationmetadata)) {
        return NO_importS;
    }
    AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationmetadata);
    return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(Annotationmetadata annotationmetadata) {
    if (!isEnabled(annotationmetadata)) {
        return EMPTY_ENTRY;
    }
    AnnotationAttributes attributes = getAttributes(annotationmetadata);
    //加载各个组件jar下的"meta-INF/spring.factories"
    List configurations = getCandidateConfigurations(annotationmetadata, attributes);
    configurations = removeDuplicates(configurations);
    Set exclusions = getExclusions(annotationmetadata, attributes);
    checkExcludedClasses(configurations, exclusions);
    configurations.removeAll(exclusions);
    configurations = getConfigurationClassFilter().filter(configurations);
    fireAutoConfigurationimportEvents(configurations, exclusions);
    return new AutoConfigurationEntry(configurations, exclusions);
}

Return the auto-configuration class names that should be considered选择要进行加载的全路径类名,默认加载spring.factories文件下org.springframework.boot.autoconfigure.EnableAutoConfiguration=下面的所有类,meta-INF/spring.factories长这样:

protected List getCandidateConfigurations(Annotationmetadata metadata, AnnotationAttributes attributes) {
    //就是默认即将要选择加载的全类名集合
    List 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;
}

代码中的configurations其实就是这些全类名集合,可以在这里打上断点以调试模式启动,可以看到里面的内容就是spring.factories里面的内容

该方法在springboot启动流程——bean实例化前被执行,返回要实例化的类信息列表;

实际上,除了org.springframework.boot.autoconfigure.EnableAutoConfiguration,spring.factories里面还定义了很多其他bean。
所有的这些bean都是通过Spring工厂加载器SpringFactoriesLoader进行加载。

**2)Spring工厂加载器SpringFactoriesLoader

它更多是提供一种配置查找的功能支持,即根据@EnableAutoConfiguration的完整类名org.springframework.boot.autoconfigure.EnableAutoConfiguration作为查找的Key,获取对应的一组@Configuration类

通过下面的这个方法来获取要再加载的类:

public static List loadFactoryNames(Class factoryType, @Nullable ClassLoader classLoader) {
		ClassLoader classLoaderToUse = classLoader;
    if (classLoaderToUse == null) {
        classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
    }
    String factoryTypeName = factoryType.getName();
    //他的key就是我们的注解类名,如“EnableAutoConfiguration”,通过key从map中取出我们的目标类名集合
    return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}


//以Map>形式返回spring.factories文件信息,内容如下面截图
private static Map> loadSpringFactories(ClassLoader classLoader) {
    Map> result = cache.get(classLoader);
    if (result != null) {
        return result;
    }

    result = new HashMap<>();
    try {
        Enumeration 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;
}


如果获取到类信息,spring可以通过类加载器将类加载到jvm中,现在我们已经通过spring-boot的starter依赖方式依赖了我们需要的组件,那么这些组件的类信息在select方法中就可以被获取到。

所以,@EnableAutoConfiguration自动配置的魔法其实就变成了:

从classpath中搜寻所有的meta-INF/spring.factories配置文件,并将其中org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的配置项通过反射(Java Refletion)实例化为对应的标注了@Configuration的JavaConfig形式的IoC容器配置类,然后汇总为一个并加载到IoC容器。

参考文章1
参考文章2

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号