官网:https://spring.io/projects/spring-boot
1. Springboot是什么
一个敏捷易用、功能强大、应用广泛的java开发框架
2. 为什么要有Springboot
- 需求:开发中我们希望能够简单快速的构建我们的项目,我们希望能更多的关注于我们的业务,而不是繁琐的配置等
- 演变:Spring->Springboot->Springcloud等
- 实际: Springboot来源于Spring,所以它继承了Spring的两大核心功能(IOC/AOP),然后它通过起步依赖、自动配置等方式,来简化Spring里面繁琐的配置和依赖管理,达到敏捷易用的目的,同时由它催生出以后的Springcloud框架
3. Spring与Springboot开发对比
- 使用Spring开发一个web项目
1.创建项目,加入依赖,并解决版本问题
2.配置web配置文件
3.配置Spring配置文件/Spring-mvc配置文件
4.编写代码
5.使用tomcat等部署
- 使用Springboot开发一个web项目
1.创建项目,选择依赖
2.编写代码并启动
4. Springboot重要功能的分析(以快速创建的web工程为例,创建过程参见下一篇博客)
1. 起步依赖
在使用ssm框架进行web开发时,我们必须手动添加依赖,不仅需要配置web依赖,还要配置web所需要的周边依赖比如json格式,mvc模型等相关依赖,并且如果导入的依赖之间有版本冲突,我们还需要自己解决这些冲突,而使用springboot你只需要添加一个spring-boot-starter-web依赖不需要写版本号,其余的步骤,springboot都为我们做好了,那springboot是怎么样将web工程所需要的周边依赖加入程序中以及如何处理这些依赖冲突的呢?(1-3说明为什么不需要写版本号及解决冲突,4-5说明为啥不需要写周边依赖)
代码如下(都在pom文件中)
<!--1.首先所有的springboot工程都会有如下的父依赖-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<!--2.点击进入此父依赖之后,会发现此pom里面会有属性、监听、资源文件路径、开发插件、配置等相关的设置,但是最重要的是spring-boot-starter-parent依然有父依赖spring-boot-dependencies-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.3.RELEASE</version>
</parent>
<!--3.点击进入spring-boot-dependencies之后,会发现此2.3.3.RELEAS版本的pom有<properties>这里面将能相互适用的大多数依赖的版本号都加入了其中,并且在 <dependencyManagement>中排除了一些依赖冲突,这些都适用于2.3.3.RELEAS版本-->
<properties>
<activemq.version>5.15.13</activemq.version>
<antlr2.version>2.7.7</antlr2.version>
<appengine-sdk.version>1.9.81</appengine-sdk.version>
<artemis.version>2.12.0</artemis.version>
<aspectj.version>1.9.6</aspectj.version>
<assertj.version>3.16.1</assertj.version>
<atomikos.version>4.0.6</atomikos.version>
<awaitility.version>4.0.3</awaitility.version>
<bitronix.version>2.1.4</bitronix.version>
<build-helper-maven-plugin.version>3.1.0</build-helper-maven-plugin.version>
<byte-buddy.version>1.10.14</byte-buddy.version>
<caffeine.version>2.8.5</caffeine.version>
<cassandra-driver.version>4.6.1</cassandra-driver.version>
<classmate.version>1.5.1</classmate.version>
<commons-codec.version>1.14</commons-codec.version>
<commons-dbcp2.version>2.7.0</commons-dbcp2.version>
<commons-lang3.version>3.10</commons-lang3.version>
<commons-pool.version>1.6</commons-pool.version>
<commons-pool2.version>2.8.1</commons-pool2.version>
<couchbase-client.version>3.0.7</couchbase-client.version>
<db2-jdbc.version>11.5.4.0</db2-jdbc.version>
<dependency-management-plugin.version>1.0.10.RELEASE</dependency-management-plugin.version>
<derby.version>10.14.2.0</derby.version>
<dropwizard-metrics.version>4.1.12.1</dropwizard-metrics.version>
<ehcache.version>2.10.6</ehcache.version>
<ehcache3.version>3.8.1</ehcache3.version>
<elasticsearch.version>7.6.2</elasticsearch.version>
<embedded-mongo.version>2.2.0</embedded-mongo.version>
<exec-maven-plugin.version>1.6.0</exec-maven-plugin.version>
<flatten-maven-plugin.version>1.2.4</flatten-maven-plugin.version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-amqp</artifactId>
<version>${activemq.version}</version>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-blueprint</artifactId>
<version>${activemq.version}</version>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-broker</artifactId>
<version>${activemq.version}</version>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-camel</artifactId>
<version>${activemq.version}</version>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-client</artifactId>
<version>${activemq.version}</version>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-console</artifactId>
<version>${activemq.version}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--4.以上我们可知spring-boot-dependencies对各个依赖的版本进行了规定,我们回到项目的pom文件,点击我们为web工程加入的-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--5.我们会进入spring-boot-starter-web-2.3.3.RELEASE.pom文件(2.3.3跟spring-boot-dependencies同步),在此文件中我们可以看到web-starter依赖如下的周边依赖,此处springboot已经为我们配置好了-->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.3.3.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
<version>2.3.3.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.3.3.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.8.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.8.RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
总结如下:我们只需要加入一个web-starter,程序会根据spring-boot-starter-parent的父依赖spring-boot-dependencies的版本找到所有此dependencies版本下规定的web-starter版本,然后在根据此版本的web-starter找到相应的版本的周边依赖starter,并且这些starter已经被spring-boot-dependencies解决过冲突了,
即这些starter都在dependencies的版本的统一管理下,2.3.3.RELEASE的dependencies里面的下的jar包版本相互适用
2. 自动配置
在使用ssm框架进行web开发时,我们解决完依赖之后,下一步就是编写配置文件,如web.xml文件,spring相关配置文件,springmvc相关配置文件,但是用springboot新建这种项目,却不需要配置这些,springboot是如何做到,不必配置这些看起来应该是web工程必须的配置文件的呢?
代码如下
//1.首先我们找到程序的入口
@SpringBootApplication
public class SpringBootWebApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootWebApplication.class, args);
}
}
//2.我们跟踪上面的run方法,到达如下代码,代码含义是将classpath:下的文件读取到程序中作为启动参数,classpath下只有下图所示的三个相关文件,显然只有SpringBootWebApplication涉及自动配置相关内容(yml文件为空,TestController是业务代码并不是必须的),所以我们需要跟踪SpringBootWebApplication的注解
public interface ResourceLoader {
String CLASSPATH_URL_PREFIX = "classpath:";
Resource getResource(String var1);
@Nullable
ClassLoader getClassLoader();
}
//跟踪注解@SpringBootApplication到@EnableAutoConfiguration到(@AutoConfigurationPackage和@Import(AutoConfigurationImportSelector.class))
//3.先来看@AutoConfigurationPackage里面有一个@Import(AutoConfigurationPackages.Registrar.class)
//此类是AutoConfigurationPackages下的内部类,对register进行断点调试,可知此方法是将@SpringBootApplication注解修饰的启动类下的所有文件扫描到容器中
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImports(metadata));
}
}
//4.再来看@Import(AutoConfigurationImportSelector.class),
//对AutoConfigurationImportSelector进行调试,重要方法为selectImports
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
//往下走
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
//接着往下走
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
//配置类名称由getSpringFactoriesLoaderFactoryClass(),得来,继续跟踪此方法,发现这些配置名都来自于String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
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;
}
getSpringFactoriesLoaderFactoryClass()由这个方法从这个文件中取得所有的配置类的名字
然后根据上面所取得的名字,在autoconfigure的jar包下,将配置类扫描到容器中
//这个地方是扫描出来的类,这样就完成了自动配置
总结:autoconfigure的jar包是主要管理springboot自动配置的,里面的META-INF/spring.factories记录了所有需要用到的自动配置类的表单,org.springframework.boot.autoconfigure下是一个个具体的配置类,程序启动会读取表单,按照表单扫描配置类,将它们一个个加入到容器中,这样就不在需要我们进行配置了。
4.补充知识
1.Springboot还有CLI命令行开发模式,以及Actuato监控等更多的功能