JavaWeb学习中关于SpringBoot的基础知识 💯💤
SpringBoot基础
一、SprinBoot简介
¶1.1 原有Spring优缺点分析
¶1.1.1 Spring的优点分析
Spring是Java企业版(Java Enterprise Edition,JEE,也称J2EE)的轻量级代替品。无需开发重量级的Enterprise JavaBean(EJB),Spring为企业级Java开发提供了一种相对简单的方法,通过依赖注入和面向切面编程,用简单的Java对象(Plain Old Java Object,POJO)实现了EJB的功能。
¶1.1.2 Spring的缺点分析
虽然Spring的组件代码是轻量级的,但它的配置却是重量级的。一开始,Spring用XML配置,而且是很多XML配置。Spring 2.5引入了基于注解的组件扫描,这消除了大量针对应用程序自身组件的显式XML配置。Spring 3.0引入了基于Java的配置,这是一种类型安全的可重构配置方式,可以代替XML。
所有这些配置都代表了开发时的损耗。因为在思考Spring特性配置和解决业务问题之间需要进行思维切换,所以编写配置挤占了编写应用程序逻辑的时间。和所有框架一样,Spring实用,但与此同时它要求的回报也不少。
除此之外,项目的依赖管理也是一件耗时耗力的事情。在环境搭建时,需要分析要导入哪些库的坐标,而且还需要分析导入与之有依赖关系的其他库的坐标,一旦选错了依赖的版本,随之而来的不兼容问题就会严重阻碍项目的开发进度。
¶1.2 SpringBoot的概述
¶1.2.1 SpringBoot解决上述Spring的缺点
SpringBoot对上述Spring的缺点进行的改善和优化,基于约定优于配置的思想,可以让开发人员不必在配置与逻辑业务之间进行思维的切换,全身心的投入到逻辑业务的代码编写中,从而大大提高了开发的效率,一定程度上缩短了项目周期。
¶1.2.2 SpringBoot的特点
- 为基于Spring的开发提供更快的入门体验。
- 开箱即用,没有代码生成,也无需XML配置。同时也可以修改默认值来满足特定的需求。
- 提供了一些大型项目中常见的非功能性特性,如嵌入式服务器、安全、指标,健康检测、外部配置等。
- 🎯SpringBoot不是对Spring功能上的增强,而是提供了一种快速使用Spring的方式。
¶1.2.3 SpringBoot的核心功能
-
起步依赖
起步依赖本质上是一个Maven项目对象模型(Project Object Model,POM),定义了对其他库的传递依赖,这些东西加在一起即支持某项功能。
简单的说,起步依赖就是将具备某种功能的坐标打包到一起,并提供一些默认的功能。
-
自动配置
Spring Boot的自动配置是一个运行时(更准确地说,是应用程序启动时)的过程,考虑了众多因素,才决定Spring配置应该用哪个,不该用哪个。该过程是Spring自动完成的。
二、SpringBoot快速入门
¶2.1 通过IDEA创建
-
使用idea工具创建一个新的工程并选择Spring Initial。
-
根据流程选择下一步,在Dependence中选择需要的模块,如web-web。
-
最后自动生成项目。
-
通过idea快速创建的SpringBoot项目的pom.xml中已经导入了我们选择的web的起步依赖的坐标
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.4.1</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>cn.sucrelt</groupId> <artifactId>sprintboot-all</artifactId> <version>0.0.1-SNAPSHOT</version> <name>sprintboot-all</name> <description>projects for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
¶2.2 Spring Boot启动类
项目自动生成一个SpringBoot启动类
@SpringBootApplication
public class HelloWorldApplication {
public static void main(String[] args) {
SpringApplication.run(HelloWorldApplication.class, args);
}
}
¶2.2.1 启动类代码分析
@SpringBootApplication
:标注SpringBoot的启动类。SpringApplication.run(HelloWorldApplication.class, args);
代表运行SpringBoot的启动类,参数为SpringBoot启动类的字节码对象。
¶2.2.2 ⭐️@SpringBootApplication 注解分析
@SpringBootApplication
可以看作是 @Configuration
、@EnableAutoConfiguration
、@ComponentScan
注解的集合。这三个注解的作用分别是:
@EnableAutoConfiguration
:启用 SpringBoot 的自动配置机制。@ComponentScan
: 扫描被@Component
(@Service
,@Controller
)注解的bean,注解默认会扫描该类所在的包下所有的类。@Configuration
:允许在上下文中注册额外的bean或导入其他配置类。
@SpringBootApplication
是几个重要的注解的组合,为了省事,避免了每次开发 Spring Boot 项目都要写一些必备的注解。
¶2.3 编写Controller
新建一个 controller 文件夹,并在这个文件夹下新建一个名字叫做 HelloWorldController
的类。
@RestController
@RequestMapping("test")
public class HelloWorldController {
@GetMapping("hello")
public String sayHello() {
return "Hello World";
}
}
¶2.4测试
执行SpringBoot启动类的主方法,控制台打印日志如下:
D:\Java\jdk-8\bin\java.exe...
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.4.1)
... ...
2020-12-13 15:16:31.361 INFO 15084 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2020-12-13 15:16:31.374 INFO 15084 --- [ main] c.s.s.SprintbootAllApplication : Started SprintbootAllApplication in 2.2 seconds (JVM running for 4.157)
2020-12-13 15:16:47.585 INFO 15084 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring
... ...
-
打印日志说明启动了SpringBoot内置的Tomcat服务器,端口为8080,默认的访问根路径为’ '。
-
打开浏览器访问url地址为:
http://localhost:8080/test/hello
,页面打印Hello World字符串。
¶2.5 SpringBoot工程热部署
我们在开发中反复修改类、页面等资源,每次修改后都是需要重新启动才生效,这样每次启动都很麻烦,浪费了大量的时间,我们可以在修改代码后不重启就能生效,在 pom.xml 中添加如下配置就可以实现这样的功能,称之为热部署。
<!--热部署配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
**注意:**IDEA进行SpringBoot热部署失败原因:Intellij IEDA默认情况下不会自动编译,需要对IDEA进行自动编译的设置。
三、SpringBoot原理分析
¶3.1 依赖原理分析
起步依赖的作用就是进行依赖的传递。
-
parent标签
pom.xml中引入了spring-boot-starter-parent,其中,一部分坐标的版本、依赖管理、插件管理已经定义好,所以SpringBoot工程继承spring-boot-starter-parent后就已经具备版本锁定等配置了。
-
dependency标签
引入发spring-boot-starter-web是将web开发要使用的spring-web、spring-webmvc等坐标进行了“打包”,这样工程只要引入spring-boot-starter-web起步依赖的坐标就可以进行web开发了,同样体现了依赖传递的作用。SpringBoot简化Spring配置的原理就是将一系列相关的依赖进行打包,在工程中只要导入一个打包好的坐标即可。
¶3.2 ⭐️自动配置原理解析
@SpringBootApplication
注解相当于三个注解:
@SpringBootConfiguration
:根据源码发现底层实际上就是Configuration注解,也就是用于标注该类是一个Spring配置类,等同于XML文件。@EnableAutoConfiguration
:开启自动配置功能。@ComponentScan
:这个注解也就是Spring中的扫描注解,默认是扫描当前类所在包及其子包下的所有类,将其中的@Controller/@Service/@Component/@Repository
等注解加载到IOC容器中。
¶核心注解EnableAutoConfiguration
SpringBoot简化配置的思想是“约定大于配置”,其核心就是@EnableAutoConfiguration
注解。
¶查看注解@EnableAutoConfiguration
...
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
... ... ...
}
-
@AutoConfigurationPackage
查看该注解:
... @Import(AutoConfigurationPackages.Registrar.class) public @interface AutoConfigurationPackage { ... ... }
最终定位到Registrar类中:
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports { @Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0])); } ... }
其功能:就是将主配置类(
@SpringBootApplication
)的所在包及其子包里边的组件扫描到Spring容器中。这与
@ComponentScan
注解功能很类似,但是比如使用Spring Data JPA,会在实体类上写@Entity
注解。这个@Entity
注解由@AutoConfigurationPackage
扫描并加载,而平时开发用的@Controller/@Service/@Component/@Repository
这些注解是由ComponentScan
来扫描并加载的。
¶回到EnableAutoConfiguration
类中并进入AutoConfigurationImportSelector
类
查看源码,主要方法如下:
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
...
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
...
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
...
return new AutoConfigurationEntry(configurations, exclusions);
}
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;
}
... ...
}
selectImports
方法调用getAutoConfigurationEntry
方法,getAutoConfigurationEntry
调用getCandidateConfigurations
方法,最终进入SpringFactoriesLoader
类中并调用其中的loadFactoryNames
方法。- 在打印的提示语中显示了META-INF/spring.factory这个文件。
¶继续查看SpringFactoriesLoader类
public final class SpringFactoriesLoader {
... ...
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
... ...
return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
...
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
...
try {
Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>()).add(factoryImplementationName.trim());
...
}
...
}
...
}
...
return result;
}
}
-
通过
SpringFactoriesLoader.loadFactoryNames
调用了loadSpringFactories
方法,在该方法中使用类加载器加载了FACTORIES_RESOURCE_LOCATION
中的类。 -
查看该常量的值:
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
与之前控制台打印输出的文件目录相同,即加载的配置文件位置为
META-INF/spring.factories
。
¶配置文件
在项目中的External Libraries中,找到spring-boot-autoconfigure-2.4.1
包,在其下目录中可以发现该配置文件META-INF/spring.factories。
文件中存有以Configuration为结尾的自动配置信息的类名,SpringApplication在获取这些类名后再加载。
... ... ...
org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
... ... ...
¶⭐️总结
@EnableAutoConfiguration
注解最终调用SpringFactoriesLoader
中的loadSpringFactories
方法加载配置文件。- 配置文件的加载位置为:
FACTORIES_RESOURCE_LOCATION
,其值是META-INF/spring.factories
。 - Spring启动自动配置时会扫描jar包路径下的
META-INF/spring.factories
,使用类加载器进行加载并将其文件包装成Properties对象最终添加到容器中。
四、SpringBoot的配置文件
¶4.1 yml配置文件
SpringBoot是基于约定的,很多配置都有默认值,如果想使用自定义配置替换默认配置,就需要使用application.properties或者application.yml(application.yaml)配置文件进行配置。
SpringBoot默认会从Resources目录下加载application.properties或application.yml(application.yaml)文件,加载顺序为yml、yaml、properties,如果同时有多个配置文件,后面的配置文件会覆盖前面的。
¶4.2 配置文件语法
¶4.2.1 配置普通数据
-
语法:
key: value
-
示例代码:
-
name: sucre
-
注意:value之前有一个空格
¶4.2. 配置对象数据
-
语法:
key:
key1: value1
key2: value2
或者:
key: {key1: value1,key2: value2}
-
示例代码:
-
person: name: sucre age: 20 addr: nanjing #或者 person: {name: sucre,age: 20,addr: nanjing}
- **注意:key1前面的空格个数不限定,在yml语法中,相同缩进代表同一个级别。**
### 4.2.3 配置数组(List、Set)数据
- 语法:
key:
- value1
- value2
或者:
key: [value1,value2]
- 示例代码:
- ```yaml
city:
- beijing
- tianjin
- shanghai
- chongqing
#或者
city: [beijing,tianjin,shanghai,chongqing]
#集合中的元素是对象形式
student:
- name: zhangsan
age: 18
score: 100
- name: lisi
age: 28
score: 88
- name: wangwu
age: 38
score: 90
- 注意:value1与之间的 - 之间存在一个空格
¶4.2.4 配置Map集合与配置对象方式相同
¶4.3 配置文件与配置类的属性映射方式
¶4.3.1 使用注解@Value映射
通过@Value
注解将配置文件中的值映射到一个Spring管理的Bean的字段上
例如:
application.yml配置如下:
person:
name: zhangsan
age: 18
实体Bean代码如下:
@Controller
public class QuickStartController {
@Value("${person.name}")
private String name;
@Value("${person.age}")
private Integer age;
@RequestMapping("/quick")
@ResponseBody
public String quick(){
return "springboot 访问成功! name="+name+",age="+age;
}
}
¶4.3.2 使用注解@ConfigurationProperties映射
通过注解@ConfigurationProperties(prefix="配置文件中的key的前缀")
可以将配置文件中的配置自动与实体进行映射
application.yml配置如下:
person:
name: zhangsan
age: 18
实体Bean代码如下:
@Controller
@ConfigurationProperties(prefix = "person")
public class QuickStartController {
private String name;
private Integer age;
@RequestMapping("/quick")
@ResponseBody
public String quick(){
return "springboot 访问成功! name="+name+",age="+age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
}
⭐️注意:使用@ConfigurationProperties方式可以进行配置文件与实体字段的自动映射,但需要字段必须提供set方法才可以,而使用@Value注解修饰的字段不需要提供set方法。