Spring Boot 快速入门
一、Spring Boot 简介
Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化Spring应用
的初始搭建以及开发过程。该框架使用了特定的方式来进行配置(约定优于配置)
,从而使开发人员不再需要定义样板化的配置。
个人认为,springboot 并非全新的框架,而是对spring框架的再封装,通过 starter 的方式轻松集成第三方的框架,集成一个框架只需要引入一个 starter,去掉了 XML 的配置,全部用注解代替,然后在属性文件中配置一些值,整个集成的过程就结束了。总的来说,使用springboot框架可以非常方便、快捷的进行spring项目的开发,提高我们开发的效率。
二、springBoot特点
- 创建独立的Spring应用程序
- 嵌入的Tomcat,无需部署WAR文件
- 简化Maven配置
- 自动配置Spring
- 提供生产就绪型功能,如指标,健康检查和外部配置
- 绝对没有代码生成和对XML没有要求配置
三、使用 IDEA 的 Spring Initializr 创建 springboot 项目
使用 IDEA Spring Initializr 创建项目,可以非常方便的创建一个 spring 项目,真正做到 just run
。
1、开发环境
-
–jdk1.8:Spring Boot 推荐jdk1.7及以上;java version “1.8.0_152”
-
–maven3.x:maven 3.3以上版本;Apache Maven 3.6.1
-
–IntelliJ IDEA:IntelliJ IDEA 2020.1
使用IDEA Spring Initializr创建项目其实就是集成了springboot官网提供的
Spring Initializr模板来创建项目,是需要连接网络的,如果没有网络,建议使用maven方式创建项目。
2、点击文件 -> 新建 -> 项目
由于使用了IntelliJ IDEA官方提供的中文插件,所以显示的是中文,我发现换成中文后,自己反而好不习惯,后面已经改回英文了。。。
3、选择 Spring Initializr ,选择JDK
需要注意的是springboot只支持 jdk 1.8及以上版本
4、填写项目信息
springboot框架内嵌了Tomcat和Jetty容器,直接打成jar包也可直接运行,spring-boot-starter-web默认使用Tomcat容器运行,如需切换为Jetty容器,只需在POM文件中添加Jetty的依赖即可。
5、选择需要的依赖包
6、选择存放项目的路径
需要注意的是,这里有可能会生成项目失败,原因是因为连接https://start.spring.io/网站超时,多尝试几次即可。
7、项目创建成功
看到这个界面,我们的第一个springboot项目就创建成功了(可以直接启动,默认地址为localhost:8080)。是不是很开心。。。
四、Hello World
上面我们创建了一个springboot的项目,已经可以成功运行了,只是项目里面什么都没有,接下来我们来编写一个hello world程序,并在浏览器中访问。
1、编写HelloController
package com.xiaoming.springboot.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@RequestMapping("/hello")
public String hello() {
return "Hello World!";
}
}
2、启动程序
编写好我们的controller类,我们就可以运行,并且从浏览器访问了,是不是so easy。简直不要太简单了。
那么如何启动程序呢,我们需要找到程序的主入口类,在本篇就是SpringBootHelloApplication.class,
(需要注意的是,在一个项目中,主入口类有且只能有一个)
,运行main方法即可。
3、页面访问
在浏览器中请求 localhost:8080/hello 地址,页面就会给我们显示出 Hello World! ,到这里一个入门程序就已经完成了,各位程序猿,冲冲冲!!!
4、修改启动端口和根路径
在实际的开发中,程序的端口号是需要根据实际需求改动的,并且一般访问路径上需要带上项目的名称。此时我们需要修改springboot的默认端口和根路径的配置,springboot提供了一个全局的配置文件给我们修改springboot自动配置的默认值。配置文件的名字是固定:(本人更喜欢yml格式的配置文件)
•application.properties
•application.yml
关于配置文件就不在这里细说了,springboot配置相关的知识点我会再写一篇博客。
如何修改配置文件呢,我们只需要将我们的application.properties文件先改名为application.yml(不改也行,当然不该的格式和这里不同),然后在配置文件中加入如下代码即可:
server:
port: 8888
servlet:
context-path: /xiaoming
修改完成重新启动一下程序,可以看到我们的端口号和根路径都变成了我们配置文件中的值。
五、揭开 springboot 的神秘面纱
在上面我们创建并运行了一个 springboot 项目,我们什么都没有配置,他凭什么就能够正常运行呢?相信各位小伙伴肯定是比较疑惑的。下面我们就来探究一下 springboot 为什么能够这么便捷的运行起来。
1、POM文件
既然我们的项目是一个maven项目,那么我们就先来看看程序的 POM 文件里面长什么样吧。
1)、parent 父工程
打开 POM 文件,我们可以在文件的头部位置看到一个 parent 项目
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
这个项目有什么作用呢?
-
依赖的版本控制
我们继续查看他的POM文件,点进去可以看到他又有一个 parent 工程
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.2.7.RELEASE</version> <relativePath>../../spring-boot-dependencies</relativePath> </parent>
继续点进去,我们发现这个工程里面保存了基本的依赖和版本的定义,如下图
可以说这个项目是Spring Boot的版本仲裁中心,真正管理Spring Boot应用里面的所有依赖版本。所以我们导入依赖默认是不需要写版本(没有在这个项目里面管理的依赖自然还是需要声明版本号),妈妈再也不担心我们在开发大型项目时因为依赖冲突产生的问题了。
-
定义了 Java 编译版本为 1.8
回到 spring-boot-starter-parent 工程,还可以看到如下内容:
<properties> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <!-- 这也是为什么我们的jdk环境为什么需要1.8及以上了 --> <java.version>1.8</java.version> <resource.delimiter>@</resource.delimiter> <maven.compiler.source>${java.version}</maven.compiler.source> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.target>${java.version}</maven.compiler.target> </properties>
-
设置使用 UTF-8 格式编码。
-
执行打包操作的配置。
-
自动化的资源过滤。
-
自动化的插件配置。
-
针对 application.properties 和 application.yml 的资源过滤,包括通过 profile 定义的不同环境的配置文件,例如 application-dev.properties 和 application-dev.yml。
<resources> <resource> <filtering>true</filtering> <directory>${basedir}/src/main/resources</directory> <includes> <include>**/application*.yml</include> <include>**/application*.yaml</include> <include>**/application*.properties</include> </includes> </resource> <resource> <directory>${basedir}/src/main/resources</directory> <excludes> <exclude>**/application*.yml</exclude> <exclude>**/application*.yaml</exclude> <exclude>**/application*.properties</exclude> </excludes> </resource> </resources>
2)、启动器
接着往下查看项目导入的依赖,可以看到以下内容
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
可以发现其中有两个依赖,一个是我们自己导入的 spring-boot-starter-web 依赖,另一个则是 Spring Initializr 创建项目时自动导入的测试依赖包(细心的小伙伴是不是已经发现了我们导入的依赖都没有标明版本号)。我们主要关注我们自己导入的 spring-boot-starter-web 依赖包。
我们发现两个依赖都有类似的名称
spring-boot-starter-xxx
,其中我们称spring-boot-starter
为springboot启动器,其实就是一系列依赖描述的组合信息,当我们需要用到那个模块,我们只需要导入那个模块的启动器即可,springboot会自动的帮我们导入该模块需要的一系列组件,并且有 parent 工程定义导入组件的版本,保证我们的程序能够正常运行
点进 spring-boot-starter-web 项目的 POM 文件,不出所料,spring-boot-starter-web 启动器帮我们导入了一系列的依赖包
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.2.7.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
<version>2.2.7.RELEASE</version>
<scope>compile</scope>
</dependency>
<!-- 这里可以看出为什么我们的程序默认使用Tomcat容器运行 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.2.7.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>2.2.7.RELEASE</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<artifactId>tomcat-embed-el</artifactId>
<groupId>org.apache.tomcat.embed</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.6.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.6.RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
2、主入口类
我们开发任何一个Spring Boot项目,项目中都会有且只有一个启动类,如下
package com.xiaoming.springboot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootHelloApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootHelloApplication.class, args);
}
}
从上面代码可以看出,我们需要关注的点有两个,一个是@SpringBootApplication
注解,另一个就是main方法中的SpringApplication.run()
语句。
1)、@SpringBootApplication
@SpringBootApplication注解是Spring Boot的核心注解,它标注在某个类上就说明这个类是Spring Boot应用的主配置类,SpringBoot就应该运行这个类的main方法来启动Spring Boot应用;我们点进这个注解中可以发现@SpringBootApplication注解其实是多个注解的组合。
@Target(ElementType.TYPE) // 注解的适用范围,其中TYPE用于描述类、接口(包括包注解类型)或enum声明
@Retention(RetentionPolicy.RUNTIME) // 注解的生命周期,保留到class文件中(三个生命周期)
@Documented // 表明这个注解应该被javadoc记录
@Inherited // 子类可以继承该注解
@SpringBootConfiguration // 继承了Configuration,表示当前是注解类
@EnableAutoConfiguration // 开启springboot的注解功能,springboot的四大神器之一,其借助@import的帮助
@ComponentScan(excludeFilters = { // 包扫描路径
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class)
})
public @interface SpringBootApplication {
...
}
虽然定义使用了7个注解进行了原信息标注,但实际上重要的只有三个注解:
- @SpringBootConfiguration(点开查看发现里面还是应用了@Configuration注解)
- @EnableAutoConfiguration
- @ComponentScan
我们可以这样认为:@SpringBootApplication ≈ @Configuration + @EnableAutoConfiguration + @ComponentScan
如果我们不嫌麻烦的话,我们的启动类也可以写成这样子
package com.xiaoming.springboot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
public class SpringBootHelloApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootHelloApplication.class, args);
}
}
可以看到,运行效果和@SpringBootApplication是一样
2)、@SpringBootConfiguration
@SpringBootConfiguration注解标注在某个类上,表示这是一个Spring Boot的配置类,底层使用的是@Configuration注解,需要注意的是,配置类本身也是IOC容器中的一个组件。
3)、@ComponentScan
@ComponentScan这个注解在Spring中很重要,它的功能其实就是自动扫描并加载符合条件的组件(比如@Component和@Repository等)或者bean定义,最终将这些bean定义加载到IOC容器中。
注意:如果你的其他包都在使用了@SpringBootApplication注解的main app所在的包及其下级包,则你什么都不用做,SpringBoot会自动帮你把其他包都扫描了。
如果你有一些bean所在的包,不在main app的包及其下级包,那么你需要手动加上@ComponentScan注解并指定那个bean所在的包。
所以SpringBoot的启动类最好是放在root package下,因为默认不指定basePackages。
4)、@EnableAutoConfiguration
@EnableAutoConfiguration 注解告诉Spring Boot开启自动配置功能,这样自动配置才能生效。他的底层是使用@Import注解,将所有符合自动配置条件的bean定义加载到IOC容器。
@EnableAutoConfiguration作为一个复合Annotation,其自身定义关键信息如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
...
}
其中主要的注解就是,@AutoConfigurationPackage和 @Import(EnableAutoConfigurationImportSelector.class)
-
@AutoConfigurationPackage
的底层其实也是调用了 @Import(AutoConfigurationPackages.Registrar.class) 注解@Import(AutoConfigurationPackages.Registrar.class)
的其实就是由AutoConfigurationPackages.Registrar类将主配置类所在包及下面所有子包里面的所有组件扫描并注册到IOC容器中
-
@Import(EnableAutoConfigurationImportSelector.class)
中的EnableAutoConfigurationImportSelector就是自动配置导入选择器,它来决定那些组件能够被导入进去,它会将所有需要导入的组件以全类名的方式返回,返回的组件就会别添加到IOC容器中。// springboot 获取自动配置类的源码 protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, 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 = filter(configurations, autoConfigurationMetadata); fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); }
进入源码debug查看返回了那些类,如下图
我们发现
@Import(EnableAutoConfigurationImportSelector.class)
会给容器中导入非常多的自动配置类(xxxAutoConfiguration),就是给容器中导入这个场景需要的所有组件,并配置好这些组件,有了这些自动配置类,就免去了我们手动编写配置注入功能组件的工作;在 getAutoConfigurationEntr() 方法中调用了一个非常关键的方法,就是 getCandidateConfigurations(),源代码如下
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { 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; }
该方法中的
SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader());
就是我们能够获取到这么多自动配置类的关键。SpringFactoriesLoader属于Spring框架私有的一种扩展方案,其主要功能就是从指定的配置文件META-INF/spring.factories加载配置。
配合@EnableAutoConfiguration使用的话,它更多是提供一种配置查找的功能支持,即根据@EnableAutoConfiguration的完整类名org.springframework.boot.autoconfigure.EnableAutoConfiguration作为查找的Key,获取对应的一组@Configuration类。我们去到源代码查看,如下图
所以,@EnableAutoConfiguration自动配置就是:从classpath中搜寻所有的META-INF/spring.factories配置文件,并将其中org.springframework.boot.autoconfigure.EnableutoConfiguration对应的配置项通过反射实例化为对应的标注了@Configuration的JavaConfig形式的IOC容器配置类,然后汇总为一个并加载到IOC容器中。
(详细的自动配置原理,我会放到springboot配置相关的博客中讲解的,边学习springboot边写博客,让自己理解的更加透彻一点,嘻嘻嘻)在上面编写的Hello World程序中我们添加了spring-boot-starter-web启动器,程序会自动添加Tomcat和Spring MVC的依赖,然后Spring Boot就会对Tomcat和Spring MVC进行自动配置。
看到这里各位小伙伴心中的疑惑是不是已经解开了呢?明明我们什么都没有配置,程序却能正常运行,其实和我们用spring开发时的配置相比一样没少,只不过是springboot将以前需要我们程序猿配置的东西,自动配置好了,真正做到了 “just run”。这就是 springboot 这么火爆的理由之一吧。
感谢你看到这里