SpringBoot是Spring技术栈的一个大整合,J2EE开发的一站式解决方案。官网提供的HelloWorld程序使用了极少的代码与配置即实现了我们以前繁琐的Spring配置。这其中的奥秘是什么呢?今天我们一起探究一下
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
@ResponseBody
@RestController
public class AppController {
@RequestMapping("/hello")
public String hello(){
return "Hello World";
}
}
1.1 pom
首先看一下项目的pom文件
1.1.1 parent项目
发现在我们的pom文件中配置了父项目
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<!‐‐ 这个插件,可以将应用打包成一个可执行的jar包;‐‐>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring‐boot‐maven‐plugin</artifactId>
</plugin>
</plugins>
</build>
而我们的父项目中也引入了一个父项目
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring‐boot‐dependencies</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath>../../spring‐boot‐dependencies</relativePath>
</parent>
他来真正管理Spring Boot应用里面的所有依赖版本;
也就是我们Spring Boot的版本仲裁中心;
所以以后我们导入依赖默认是不需要写版本;(没有在dependencies里面管理的依赖自然需要声明版本号)
1.1.2 启动器
在我们得pom文件中出了配置父项目,还引入了如下配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
我们点进去发现,发现已经帮我们引入了web所需要的依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.1.0.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
<version>2.1.0.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.1.0.RELEASE</version>
<scope>compile</scope>
</dependency>
......
spring-boot-starter 也就是spring-boot场景启动器;帮我们导入了web模块正常运行所依赖的组件
Spring Boot将所有的功能场景都抽取出来,做成一个个的starters(启动器),只需要在项目里面引入这些starter
相关场景的所有依赖都会导入进来。要用什么功能就导入什么场景的启动器
1.2 主程序类,主入口类
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
在我们主程序类上标注了@SpringBootApplication 注解
@SpringBootApplication: Spring Boot应用标注在某个类上说明这个类是SpringBoot的主配置类,SpringBoot
就应该运行这个类的main方法来启动SpringBoot应用
接下来我们看一下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 {
可以看到首先他包含@SpringBootConfiguration
注解 和 @EnableAutoConfiguration
注解
1.2.1 @SpringBootConfiguration
@SpringBootConfiguration:Spring Boot的配置类; 标注在某个类上,表示这是一个Spring Boot的配置类
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration 表明这是一个配置类
public @interface SpringBootConfiguration {
}
1.2.2 @EnableAutoConfiguration
我们再看一下@SpringBootApplication中的另一个注解@EnableAutoConfiguration
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
@EnableAutoConfiguration 注解包括@AutoConfigurationPackage
注解 和 @Import({AutoConfigurationImportSelector.class})
注解
1.2.2.1 @AutoConfigurationPackage
其中@AutoConfigurationPackage注解代码为:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class}) Spring的底层注解@Import,给容器中导入一个组件
public @interface AutoConfigurationPackage {
}
AutoConfigurationPackage 引入了 AutoConfigurationPackages.Registrar.class
我们点击查看Registrar.class
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
}
发现new AutoConfigurationPackages.PackageImport(metadata)).getPackageName()
得值正好是我们@SpringBootApplication标注的主程序类所在的包名
也就是说在主程序类包下面的类会被扫描当做组件注册到spring容器中,这也就是为什么我们没有像以前那样配置包扫描,配置bean,我们的controller也被加入了spring容器
1.2.2.2 AutoConfigurationImportSelector
然后我们接着看 @EnableAutoConfiguration 注解中另一个
@Import({AutoConfigurationImportSelector.class}) (Import XXX 相当于引入了 XXX组件)
在AutoConfigurationImportSelector类getAutoConfigurationEntry方法中
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.filter(configurations, autoConfigurationMetadata);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
可以看到该方法的返回值是 XXXAutoConfigration的 配置的集合
也就是说springboot 自动帮我们自动注册了这些组件,但是这些组件是在哪获取的值呢?我们接着看getAutoConfigurationEntry方法中调用的getCandidateConfigurations 方法,
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
}
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
在方法中我们调用loadFactoryNames并传入了EnableAutoConfiguration.class 和 类加载器
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
也就是在我们jar包中的**“META-INF/spring.factories”** 配置了EnableAutoConfiguration的值,我们去看一下
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\
所以我们springBoot项目启动的时候会从**“META-INF/spring.factories”** 中获取EnableAutoConfiguration的值然后帮我们自动配置的组件
因此,我们一个简简单单没有任何配置的HelloWorld的原理就简单解析完了
- pom文件中的父项目 负责控制版本,作为版本仲裁
- 我们可以引入我们需要的场景启动器,启动器会帮我们引入需要的依赖,也就是封装了一层
- SpringBoot启动的时候加载主配置类,开启了自动配置功能 @EnableAutoConfiguration
- @EnableAutoConfiguration将 类路径下 META-INF/spring.factories 里面配置的所有EnableAutoConfiguration的值加入到了容器中