【SpringBoot】- starter 机制 -- @SpringBootApplication

  • 意义

springboot 框架能够快速高效的构建一个基于 spirng 框架以及 spring 生态 体系的应用解决方案。它是对“约定优于配置”这个理念下的一个最佳实践。因此它是一个服务于框架的框架,服务的范围是简化配置文件。

·约定优于配置的体现

·maven 的目录结构

               a) 默认有 resources 文件夹存放配置文件

               b) 默认打包方式为 jar

·spring-boot-starter-web 中默认包含 springmvc相关依赖以及内置的tomcat容器,使得构建一个web应用更加简单

·默认提供 application.properties/yml 文件

·默认通过 spring.profiles.active 属性来决定运行环境时读取的配置文件

·EnableAutoConfiguration 默认对于依赖的 starter 进行自动装载

  • @SpringBootApplication

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication

    ·@SpringBootConfiguration

@Configuration
public @interface SpringBootConfiguration {
}

spring3基于java5的Annotations特性,推出了基于Java代码和Annotation元信息的依赖关系绑定描述的方式,也就是 JavaConfig。任何一个标注了@Configuration 的 Java 类定义都是一个JavaConfig 配置类。而在这个配置类中,任何标注了@Bean 的方法,它的返回值都会作为 Bean 定义注册到Spring 的 IOC 容器,方法名默认成为这个 bean 的 id。

    ·@ComponentScan

相当于 xml 配置文件中的。 主要作用就是扫描指定路径下的标识了需要装配的类,自动装配到 spring 的 Ioc 容器中。标识需要装配的类的形式主要是:@Component、@Repository、@Service、@Controller 这类的注解标识的类。

    ·@EnableAutoConfiguration

@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class}) // 使用@Import导入 AutoConfigurationImportSelector
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class[] exclude() default {};

    String[] excludeName() default {};
}

            · @AutoConfigurationPackage

                动态注册bean定义,当前主程序类的同级以及子级的包下的class

@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}
    static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
        Registrar() {
        }

        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
        }

        public Set determineImports(AnnotationMetadata metadata) {
            return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
        }
    } 
  

                     · @Import({AutoConfigurationImportSelector.class})

                          AutoConfigurationImportSelector 继承图如下

【SpringBoot】- starter 机制 -- @SpringBootApplication_第1张图片

                            -> selectImports -> SpringFactoriesLoader.loadFactoryNames() -> loadSpringFactories()

public abstract class SpringFactoriesLoader {

    // 这里的第一个参数是 EnableAutoConfiguration.class
    public static List loadFactoryNames(Class factoryClass, @Nullable ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
    }

    private static Map> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap result = (MultiValueMap)cache.get(classLoader);
        if (result != null) {
            return result;
        } else {
            try {
                // 加载配置资源 META-INF/spring.factories 
                Enumeration 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();
                        List factoryClassNames = Arrays.asList(StringUtils.commaDelimitedListToStringArray((String)entry.getValue()));
                        result.addAll((String)entry.getKey(), factoryClassNames);
                    }
                }

                cache.put(classLoader, result);
                return result;
            } catch (IOException var9) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var9);
            }
        }
    }

}

最终:从 META-INF/spring.factories 获取以 EnableAutoConfiguration 为key 的配置类的全类名,最终被纳入 spring 容器

 

  • @Import

综上可知:@Import 注解可以配置三种不同的 class

  • 基于普通 bean 或者带有@Configuration 的 bean 进行注入
  • 实现 ImportSelector 接口进行动态注入 -> @Import({AutoConfigurationImportSelector.class})
  • 实现 ImportBeanDefinitionRegistrar 接口进行动态注入 -> @Import({Registrar.class})

 

  • 条件过滤

在分析 AutoConfigurationImportSelector 的源码时,会先扫描 spring-autoconfiguration-metadata.properties文件,最后在扫描 spring.factories 对应的类时,会结合前面的元数据进行过滤,原因是很多的@Configuration 其实是依托于其他的框架来加载的, 如果当前的 classpath 环境下没有相关联的依赖,则意味 着这些类没必要进行加载,所以,通过这种条件过滤可以 有效的减少@configuration 类的数量从而降低SpringBoot 的启动时间。

Conditions相关注解 描述
@ConditionalOnBean 在存在某个 bean 的时候
@ConditionalOnMissingBean 不存在某个 bean 的时候
@ConditionalOnClass 当前 classpath 可以找到某个类型的类时
@ConditionalOnMissingClass 当前 classpath 不可以找到某个类型的类时
@ConditionalOnResource 当前 classpath 是否存在某个资源文件
@ConditionalOnProperty 当前 jvm 是否包含某个系统属性为某个值
@ConditionalOnWebApplication 当前 spring context 是否是web应用程序

 

 

你可能感兴趣的:(springboot,java面试)