传统的Spring框架实现一个Web服务,需要导入各种依赖JAR包,然后编写对应的XML配置文件等;相比较而言SpringBoot更加方便、快捷和高效。
SpringBoot依赖管理
两个核心依赖:
Spring-boot-starter-parent
Spring-boot-starter-web
Spring-boot-starter-parent:进行版本统一管理
<!--Spring Boot 父项目依赖管理-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
...
<!--Ctrl进入查看,其有一个父依赖spring-boot-dependencies-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.4.RELEASE</version>
</parent>
...
<!--继续进入查看spring-boot-dependencies底层源文件-->
在<properties>标签内,对常用技术框架的依赖文件进行了统一版本号管理,常用的比如
tomcat、spring、activemp等,都有与Springboot相匹配的版本,所以在我们使用pom.xml
时不需要标注依赖文件版本号。
注意:
如果pom.xml引入的依赖文件不是spring-boot-starter-parent所管理的,比如Mybatis、
Druid技术框架,那么在引入依赖文件时,需要使用<version>标签指定依赖文件的版本号。
Spring-boot-starter-web: 该依赖启动器提供了Web开发场景所需的底层所有依赖文件,它对Web开发场景所需的依赖文件进行了统一管理
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
...
进入spring-boot-starter-web源码:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.3.4.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
<version>2.3.4.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.3.4.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.9.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.9.RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
- 可以看出,仅仅在pom.xml文件中引入spring-boot-starter-web依赖启动器,就可以实现Web的场景开发,无须额外导入Tomcat服务器以及其他Web依赖文件等。
- 当然,这些引入的依赖文件的版本号还是由Spring-boot-starter-parent父依赖进行统一管理。
Spring自动配置
@SpringBootApplication注解
@SpringBootApplication注解标注在Spring Boot启动入口处,查看该注解源码
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
@SpringBootConfiguration注解:表示SpringBoot配置类,标识一个可以被组件扫描器扫描的配置类。
其内部有一个@Configuration注解,该注解是由Spring框架提供的,表示当前类为一个配置类,并可以被组件扫描器扫描。
@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 {};
}
同样为一组合注解
@AutoConfigurationPackage:
查看其源码,可以发现其功能由@Import注解实现,作用是向容器导入注册的所有组件,导入的组件由Registry决定。
该注解的主要作用是获取项目主程序启动类所在根目录,从而指定后续组件扫描器要扫描的包位置。
@Import({AutoConfigurationImportSelector.class})
如下核心方法:筛选出当前项目环境需要启动的自动配置类XxxAutoConfiguration,从而实现当前项目运行所需的自动配置环境。
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
//this.getCandidateConfigurations(annotationMetadata, attributes)方法
//从META-INF/spring-factories中获取所有候选自动配置类
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);
//this.getConfigurationClassFilter().filter(configurations)方法
//对所有候选配置类进行筛选,根据pom.xml文件筛选出当前项目环境的自动配置类
configurations = this.getConfigurationClassFilter().filter(configurations);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
-
@ComponentScan注解:是一个组件包扫描器,用于将指定包中的注解类自动装配到Spring的Bean容器中,其具体扫描的包的根路径由Spring Boot项目主程序启动类所在包位置决定,由@AutoConfigurationPackage注解解析得到主程序启动类所在包的具体位置。
Spring自动执行流程
- SpringApplication实例的初始化
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class[]{primarySource}, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = new HashSet();
this.isCustomEnvironment = false;
this.lazyInitialization = false;
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
//判断当前webApplicationType的应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//设置SpringApplication应用初始化器,使用类加载器SpringFactoriesLoader从Spring.factiories文件中获取
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
//设置SpringApplication的应用监听器,获取方式同上
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
//推断并设置项目main()方法启动的主程序类
this.mainApplicationClass = this.deduceMainApplicationClass();
}
- 项目的初始化启动
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
this.configureHeadlessProperty();
//1.获取初始化的SpringApplicationRunListeners 运行监听器并运行
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
Collection exceptionReporters;
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//2.项目和运行环境environment 预设置
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
Banner printedBanner = this.printBanner(environment);
//3.项目应用上下文ApplicationContext的预配置
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
//4.由项目运行监听器启动配置好的应用上下文ApplicationContext
listeners.started(context);
//5.调用应用上下文ApplicationContext中配置的程序执行器XxxRunner
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
//6.由项目运行监听器持续运行配置好的应用上下文ApplicationContext
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}