一、SpringBoot初体验
一个典型的SpringBoot应用长什么样子呢?如果我们使用http://start.spring.io/创建一个最简单的依赖Web模块的SpringBoot应用,一般情况下, 我们会得到一个SpringBoot应用的启动类,如下面代码所示:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;©SpringBootApplication
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args); }
}
所有的SpringBoot无论怎么定制,本质上与上面的启动类代码是一样的,而以上代码示例中,**Annotation定义(@SpringBootApplication)
和类定义 (SpringApplication.run)
**最为耀眼,那么要揭开SpringBoot应用的奥秘,很明显的,我们只要先从这两位开始就可以了。
二、@SpringBootApplication背后的秘密
@SpringBootApplication是一个"三体”结构,实际上它是一个复合 Annotation:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(...)
public @interface SpringBootApplication{
...
}
虽然定义使用了多个Annotation进行了原信息标注,但实际上重要的只有三个Annotation:
- @Configuration(@SpringBootConfiguration点开查看发现里面还是应用了@Configuration)
- @EnableAutoConfiguration
- @ComponentScan
所以,如果我们使用如下的SpringBoot启动类,整个SpringBoot应用依然可以与之前的启动类功能对等:
@Configuration
@EnableAutoConfiguration
@ComponentScan
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}
但每次都写三个Annotation显然过于繁琐,所以写一个@SpringBoot-Application这样的一站式复合Annotation显然更方便些。
1、@Configuration 创世纪
很多SpringBoot的代码示例都喜欢在启动类上直接标注@Configuration或者@SpringBootApplication,对于初接触SpringBoot的开发者来说,其实这种做法不便于理解,如果我们将上面的SpringBoot启动类拆分为两个独立的Java类,整个形势就明朗了:
@Configuration
@EnableAutoConfiguration
@ComponentScan
public class DemoConfiguration {@Beanpublic Controller controller() {return new Controller();}
}
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoConfiguration.class, args);}
}
2、@EnableAutoConfiguration的功效
@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 {};
}
其中最关键的要属@Import(AutoConfigurationImportSelector.class)
,借助AutoConfigurationImportSelector
,@EnableAutoConfiguration
可以帮助SpringBoot应用将所有符合条件的@Configuration
配置都加载到当前SpringBoot创建并使用的IoC容器。
借助于Spring框架原有的一个工具类:SpringFactoriesLoader的支持,@EnableAutoConfiguration
可以智能的自动配置功效才得以大功告成!
SpringFactoriesLoader 自动配置幕后英雄
Spring的SpringFactoriesLoader工厂的加载机制类似java提供的SPI机制一样,是Spring提供的一种加载方式。只需要在classpath路径下新建一个文件META-INF/spring.factories,并在里面按照Properties格式填写好接口和实现类即可通过SpringFactoriesLoader来实例化相应的Bean。配置的格式为Key = Value形式,其中key可以是接口、注解、或者抽象类的全名。value为相应的实现类,当存在多个实现类时,用“,”进行分割。
对于 @EnableAutoConfiguration 来说,SpringFactoriesLoader 的用途稍 微不同 一些,其本意是为了提供SPI扩展的场景,而在@EnableAutoConfiguration的场景 中,它更多是提供了一种配置查找的功能支持,即根据@EnableAutoConfiguration 的完整类名 org.springframework.boot.autoconiigure.EnableAutoConfiguration 作为查找 的Key,获取对应的一组©Configuration类。
所以,@EnableAutoConfiguration自动配置的魔法其实就变成了:从 classpath 中搜寻所有 META-INF/spring.factories 配置文件,并将其中 org.spring・ framework.boot.autoconfigure.EnableAutoConfiguration 对应的配置项通过反射 (Java Reflection)实例化为对应的标注了 ©Configuration的JavaConfig形式的 IoC容器配置类,然后汇总为一个并加载到IoC容器。
3、@ComponentScan
@ComponentScan的功能其实就是自动扫描并加载符合条件的组件或bean定义,最终将这些bean定义加载到容器中。加载bean定义到Spring的IoC容器,当然我们也可以手工单个注册
参考:
- 《SpringBoot揭秘+快速构建微服务体系》 第三章