《SpringBoot揭秘:快速构建微服务体系》—第3章3.2节@SpringBootApplication背后的秘密...

    xiaoxiao2023-12-22  158

    本节书摘来自华章出版社《SpringBoot揭秘:快速构建微服务体系》一书中的第3章,第3.2节@SpringBootApplication背后的秘密,作者王福强,更多章节内容可以访问云栖社区“华章计算机”公众号查看。

    3.2 @SpringBootApplication背后的秘密@SpringBootApplication是一个“三体”结构,实际上它是一个复合Annotation:

    @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Configuration @EnableAutoConfiguration @ComponentScan public @interface SpringBootApplication{ ... }

    虽然它的定义使用了多个Annotation进行元信息标注,但实际上对于SpringBoot应用来说,重要的只有三个Annotation,而“三体”结构实际上指的就是这三个Annotation:@Configuration@EnableAutoConfiguration@ComponentScan所以,如果我们使用如下的SpringBoot启动类,整个SpringBoot应用依然可以与之前的启动类功能对等:

    @Configuration @EnableAutoConfiguration @ComponentScan public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }

    但每次都写三个Annotation显然过于繁琐,所以写一个@SpringBoot-Application这样的一站式复合Annotation显然更方便些。3.2.1 @Configuration创世纪这里的@Configuration对我们来说并不陌生,它就是JavaConfig形式的Spring IoC容器的配置类使用的那个@Configuration,既然SpringBoot应用骨子里就是一个Spring应用,那么,自然也需要加载某个IoC容器的配置,而SpringBoot社区推荐使用基于JavaConfig的配置形式,所以,很明显,这里的启动类标注了@Configuration之后,本身其实也是一个IoC容器的配置类!很多SpringBoot的代码示例都喜欢在启动类上直接标注@Configuration或者@SpringBootApplication,对于初接触SpringBoot的开发者来说,其实这种做法不便于理解,如果我们将上面的SpringBoot启动类拆分为两个独立的Java类,整个形势就明朗了:

    @Configuration @EnableAutoConfiguration @ComponentScan public class DemoConfiguration { @Bean public Controller controller() { return new Controller(); } } public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoConfiguration.class, args); } }

    所以,启动类DemoApplication其实就是一个标准的Standalone类型Java程序的main函数启动类,没有什么特殊的。而@Configuration标注的DemoConfiguration定义其实也是一个普通的JavaConfig形式的IoC容器配置类,没啥新东西,全是Spring框架里的概念!3.2.2 @EnableAutoConfiguration的功效@EnableAutoConfiguration其实也没啥“创意”,各位是否还记得Spring框架提供的各种名字为@Enable开头的Annotation定义?比如@EnableScheduling、@EnableCaching、@EnableMBeanExport等,@EnableAutoConfiguration的理念和“做事方式”其实一脉相承,简单概括一下就是,借助@Import的支持,收集和注册特定场景相关的bean定义:@EnableScheduling是通过@Import将Spring调度框架相关的bean定义都加载到IoC容器。@EnableMBeanExport是通过@Import将JMX相关的bean定义加载到IoC容器。而@EnableAutoConfiguration也是借助@Import的帮助,将所有符合自动配置条件的bean定义加载到IoC容器,仅此而已!@EnableAutoConfiguration作为一个复合Annotation,其自身定义关键信息如下:

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

    其中,最关键的要属@Import(EnableAutoConfigurationImportSelector.class),借助EnableAutoConfigurationImportSelector,@EnableAutoConfiguration可以帮助SpringBoot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器,就跟一只“八爪鱼”一样(如图3-1所示)。借助于Spring框架原有的一个工具类:SpringFactoriesLoader的支持,@EnableAutoConfiguration可以“智能”地自动配置功效才得以大功告成!

    自动配置的幕后英雄:SpringFactoriesLoader详解 SpringFactoriesLoader属于Spring框架私有的一种扩展方案(类似于Java的SPI方案java.util.ServiceLoader),其主要功能就是从指定的配置文件META-INF/spring.factories加载配置,spring.factories是一个典型的java properties文件,配置的格式为Key = Value形式,只不过Key和Value都是Java类型的完整类名(Fully qualified name),比如: example.MyService=example.MyServiceImpl1,example.MyServiceImpl2 然后框架就可以根据某个类型作为Key来查找对应的类型名称列表了: public abstract class SpringFactoriesLoader { // ... public static <T> List<T> loadFactories(Class<T> factoryClass, ClassLoader classLoader) { ... } public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) { ... } // ... }

    对于@EnableAutoConfiguration来说,SpringFactoriesLoader的用途稍微不同一些,其本意是为了提供SPI扩展的场景,而在@EnableAutoConfiguration的场景中,它更多是提供了一种配置查找的功能支持,即根据@EnableAutoConfiguration的完整类名org.springframework.boot.autoconfigure.EnableAutoConfiguration作为查找的Key,获取对应的一组@Configuration类:org.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.springframework.boot.autoconfigure.admin.SpringApplicationAdmin- JmxAutoConfiguration,\org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\org.springframework.boot.autoconfigure.MessageSourceAutoConfiguration,\org.springframework.boot.autoconfigure.PropertyPlaceholderAuto- Configuration,\org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\org.springframework.boot.autoconfigure.cassandra.CassandraAuto-Configuration,\org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\org.springframework.boot.autoconfigure.context.ConfigurationProperties-AutoConfiguration,\org.springframework.boot.autoconfigure.dao.PersistenceException-TranslationAutoConfiguration,\org.springframework.boot.autoconfigure.data.cassandra.Cassandra-DataAutoConfiguration,\org.springframework.boot.autoconfigure.data.cassandra.Cassandra-RepositoriesAutoConfiguration,\...以上是从SpringBoot的autoconfigure依赖包中的META-INF/spring.factories配置文件中摘录的一段内容,可以很好地说明问题。所以,@EnableAutoConfiguration自动配置的魔法其实就变成了:从classpath中搜寻所有META-INF/spring.factories配置文件,并将其中org.spring-framework.boot.autoconfigure.EnableAutoConfiguration对应的配置项通过反射(Java Reflection)实例化为对应的标注了@Configuration的JavaConfig形式的IoC容器配置类,然后汇总为一个并加载到IoC容器。目前为止,还是Spring框架的原有概念和支持,依然没有“新鲜事”!3.2.3 可有可无的@ComponentScan为啥说@ComponentScan是可有可无的?因为原则上来说,作为Spring框架里的“老一辈革命家”,@ComponentScan的功能其实就是自动扫描并加载符合条件的组件或bean定义,最终将这些bean定义加载到容器中。加载bean定义到Spring的IoC容器,我们可以手工单个注册,不一定非要通过批量的自动扫描完成,所以说@ComponentScan是可有可无的。对于SpringBoot应用来说,同样如此,比如我们本章的启动类:

    @Configuration @EnableAutoConfiguration @ComponentScan public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }

    如果我们当前应用没有任何bean定义需要通过@ComponentScan加载到当前SpringBoot应用对应使用的IoC容器,那么,除去@ComponentScan的声明,当前SpringBoot应用依然可以照常运行,功能对等!看,还是没有啥新东西!

    相关资源:敏捷开发V1.0.pptx
    最新回复(0)