美文网首页
这一切,从注解SpringBootApplication开始

这一切,从注解SpringBootApplication开始

作者: 我也有键盘 | 来源:发表于2020-02-18 10:19 被阅读0次

    深入学习springboot ,记录学习心得,第一篇

    1. 什么是springboot

    ​ springboot 是一个服务于spring框架的框架。spring的核心IoC(控制反转)、DI(依赖注入)用起来实在是太方便了;但是使用spring需要很多繁琐的配置。springboot简化了这些配置,是“约定优于配置”的最佳实践。

    2. springboot体现了哪些约定

    1. maven项目的目录结构

      默认包含resource的文件夹存放配置文件

      默认打jar包

    2. spring-boot-starter-web 包中默认包含spring mvc相关依赖以及内置Tomcat容器,构建web应用更简单

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

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

    5. EnableAutoConfiguration默认对依赖的starter自动装载

    3. 注解SpringBootApplication干了什么

    springboot工程的main函数所在类加了@SpringBootApplication,先看看它的定义:

    @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 
    

    SpringBootApplication本质上由3个注解组成,分别是:

    • @SpringBootConfiguration, 实际上这玩意还是一个Configuration

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

    • @ComponentScan

    简单分析这三个注解。

    1. @Configuration

      看名字就知道,他是一种配置。实际上,它是JavaConfig形式的基于Spring IoC 容器在配置Bean时使用的一种注解(传统的spring容器使用xml的形式来配置Bean,现在基本都使用JavaConfig形式)。

      标注@Configuration的java类都是一个JavaConfig配置类。在这个类中,任何标注@Bean的方法都会将方法名作为Bean定义并注册到SpringIoC容器(方法名就是Bean的id)。

    2. @ComponentScan

      看名字 组件扫描。 它扫描指定路径(包含子文件夹)下的标识了需要自动装配的类,并装配到IoC容器中。

      标识:@Component、@Service、@Repository、@Controller

    3. @EnableAutoConfiguration

      看名儿、启动自动配置。这是springboot可以自动装配的关键,它帮助springboot应用把所有符合条件的@Configuration配置都加载到当前使用的IoC容器中。查看该注解的定义:

      @Target({ElementType.TYPE})
      @Retention(RetentionPolicy.RUNTIME)
      @Documented
      @Inherited
      @AutoConfigurationPackage
      @Import({AutoConfigurationImportSelector.class})
      public @interface EnableAutoConfiguration
      

      @Import({AutoConfigurationImportSelector.class})是干啥?

      AutoConfigurationImportSelector实现ImportSelector接口,帮助springboot将所有符合条件的@Configuration都加载至IoC容器中。那么什么样的配置才是符合条件的呢?

      public String[] selectImports(AnnotationMetadata annotationMetadata) {
              if (!this.isEnabled(annotationMetadata)) {
                  return NO_IMPORTS;
              } else {
                  AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
                  AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
                  return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
              }
          }
      

      点进loadMetadata()方法中击查看定义:

      return loadMetadata(classLoader, "META-INF/spring-autoconfigure-metadata.properties");
      

      META-INF/spring-autoconfigure-metadata.properties下,有

      spring-autoconfigure-metadata.png

    即,符合这些条件(Contitional)的将被自动加载到容器中,条件有如下几类:

    • @ConditionalOnClass : classpath中存在该类时起效
    • @ConditionalOnMissingClass : classpath中不存在该类时起效
    • @ConditionalOnBean : DI容器中存在该类型Bean时起效
    • @ConditionalOnMissingBean : DI容器中不存在该类型Bean时起效
    • @ConditionalOnSingleCandidate : DI容器中该类型Bean只有一个或@Primary的只有一个时起效
    • @ConditionalOnExpression : SpEL表达式结果为true时
    • @ConditionalOnProperty : 参数设置或者值一致时起效
    • @ConditionalOnResource : 指定的文件存在时起效
    • @ConditionalOnJndi : 指定的JNDI存在时起效
    • @ConditionalOnJava : 指定的Java版本存在时起效
    • @ConditionalOnWebApplication : Web应用环境下起效
    • @ConditionalOnNotWebApplication : 非Web应用环境下起效

    对于符合条件的配置通过SpringFactoriesLoader加载,大概流程是在spring.factories中以spring-autoconfigure-metadata.properties有效的的value作为作为查找key对指定的factoryClass进行加载。原理类似于java原生的SPI机制。
    举个demo:

    1. 创建maven模板工程,新增HelloService类

      public class HelloService {
          public String sayHello(String msg) {
              return "hello " + msg;
          }
      }
      
      
    1. 新建HelloConfig将HelloService注册为bean

      @Configuration
      public class HelloConfig {
          @Bean
          public HelloService helloService() {
              return new HelloService();
          }
      }
      
    2. 在resources目录下创建META-INF\spring.factories

      org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.sz.HelloConfig
      

    整个工程结构如下,spring-autoconfigure-metadata.properties文件可以先不配置

    创建依赖的jar包
    1. 将工程install到本地maven仓库,第一部分完成。

    2. 再创建一个springboot项目,将spi-dependency依赖添加进来

    3. 调用HelloService的sayHello()方法

      @SpringBootApplication
      public class Main {
          public static void main(String[] args) {
              ConfigurableApplicationContext ca= SpringApplication.run(Main.class,args);
      
              System.out.println(ca.getBean(HelloService.class).sayHello("world"));
          }
      }
      

      运行main可以看到helloService被获取到了。

      执行结果

    回到第3步,把spring.factories的配置项注释掉,重新mvn install再来执行main方法,则获取bean失败。。

    相关文章

      网友评论

          本文标题:这一切,从注解SpringBootApplication开始

          本文链接:https://www.haomeiwen.com/subject/ojnwfhtx.html