SpringBoot学习笔记01

1.Hello,World!

1.1 SpringBoot简介

回顾什么是Spring

Spring是一个开源框架,2003年兴起的轻量级Java开发框架,作者:Rod Johnson

Spring 是为了解决企业级应用开发的复杂性而创建的简化开发

Spring是如何简化Java开发的

为了简化java开发的复杂性,Spring采用了以下四种关键策略:

  1. 基于POJO的轻量级和最小侵入性编程,所有东西都是bean;
  2. 通过IOC,依赖注入(DI)和面向接口实现松耦合;
  3. 基于切面(AOP)和惯例进行声明式编程
  4. 通过切面和模板减少样式代码,Redis Template, xxxTemplate;

什么是SpringBoot

  1. 学过Javaweb的同学都知道,开发一个web的应用,从最开始接触Servlet结合Tomcat,跑出一个HelloWorld程序,是要经历特别多的步骤,后来就用了框架Struts,再后来是SpringMVC,到了现在的SpringBoot,过一两年又会有其他web框架出现
  2. 言归正传,什么是SpringBoot呢,就是一个JavaWeb的开发框架,和SpringMVC相似,对比其他Javaweb框架的好处,官方说是简化开发,约定大于配置,you can" just run",能迅速的开发web应用,几行代码开发一个http接口
  3. 所有的技术开发框架的发展似乎都遵循一条主线规律:从一个复杂应用场景衍生一种规范框架,人们只需要进行各种配置而不需要自己去实现它,这时候强大的配置功能成了优点,发展到一定程度之后,人们根据实际生产应用情况,选取其中实用功能和设计精华,重构出一些轻量级的框架,之后为了提高开发效率,嫌弃原先的各类配置过于麻烦,于是开始提倡“约定大于配置”,进而衍生出一些一站式的解决方案
  4. 是的,这就是java企业级应用->J2EE->spring->springboot的过程
  5. 随着spring的不断发展,涉及的领域越来越多,项目整合开发需要配合各种各样的文件,慢慢变得不那么易用简单,违背了最初的理念,甚至人称配置地域,SpringBoot正式在这样的一个背景下被抽象出来的开发框架,目的让大家更容易的使用Spring,更容易的集成各种常用的中间件,开源软件
  6. SpringBoot基于Spring开发,SpringBoot本身并不提供Spring框架的核心特性以及扩展功能,只是用于快速,敏捷地开发新一代基于Spring框架的应用程序,也就是说,它并不是用来替代Spring的解决方案,而是和Spring框架紧密结合用于提升Spring开发者体验的工具,SpringBoot以约定大于配置的核心思想,默默帮我们进行了很多设置,多数SpringBoot应用只需要很少的Spring配置,同时他集成了大量常用的第三方库配置,例如Redis, MongoDB, Jpa,RabbitMQ,Quartz等,SpringBoot应用中这些第三方库几乎可以零配置的开箱即用
  7. 简单来说SpringBoot其实不是什么新的框架,它默认配置了很多框架的使用方式,就像maven整合了所有的jar包,springboot整合了所有的框架
  8. SpringBoot出生名门,从一开始就站在一个比较高的起点,又经过这几年的发展,生态足够完善,springboot已经当之无愧成为Java领域最热门的技术

SpringBoot的主要优点

  • 为所有的Spring开发者更快的入门
  • 开箱即用,提供各种默认配置来简化项目配置
  • 内嵌式容器简化web项目
  • 没有冗余代码生成和XML配置的要求

1.2 Hello, World

准备工作

将学习如何创建一个SpringBoot应用,并且实现一个简单的HTTP请求处理,通过这个例子对SpringBoot又一个初步的理解,并体验其结构简单,开发快速的特性

环境准备

  • Java JDK: 1.8
  • Maven: 3.5.0
  • SpringBoot: 2.3.4

开发工具:

  • IDEA

创建基础项目说明

Spring官方提供了非常方便的工具让我们快速构建应用

Spring initializr: https://start.spring.io/

项目创建方式一:使用Spring Initialier的web页面创建项目

  1. 打开https://start.spring.io/
  2. 填写项目信息
  3. 点击”GENERATE”按钮生成项目下载此项目
  4. 解压项目包,并用IDEA以Maven项目导入,一路下一步即可,直到项目导入完毕
  5. 如果是第一次使用,包比较多,需要耐心等待一切就绪

SpringBoot学习笔记01 - 图1

项目创建方式二:使用idea直接创建项目

  1. 创建一个新项目
  2. 选择Spring Initalizr,可以看到默认就是去官网的快速构建工具那里实现
  3. 填写项目信息
  4. 选择初始化的组件(初学勾选web即可)
  5. 填写项目路径
  6. 等待项目构建成功

项目结构分析:

通过上面的步骤完成了基础项目的构建,就会自动生成以下文件

  1. 程序的主启动类
  2. 一个application.properties配置文件
  3. 一个测试类
  4. 一个pom.xml

pom.xml分析

打开pom.xml看看SpringBoot项目的依赖

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <parent>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-parent</artifactId>
  8. <version>2.3.4.RELEASE</version>
  9. <relativePath/> <!-- lookup parent from repository -->
  10. </parent>
  11. <groupId>com.jcsune</groupId>
  12. <artifactId>helloworld</artifactId>
  13. <version>0.0.1-SNAPSHOT</version>
  14. <name>helloworld</name>
  15. <description>Demo project for Spring Boot</description>
  16. <properties>
  17. <java.version>1.8</java.version>
  18. </properties>
  19. <dependencies>
  20. <dependency>
  21. <groupId>org.springframework.boot</groupId>
  22. <artifactId>spring-boot-starter-web</artifactId>
  23. </dependency>
  24. <dependency>
  25. <groupId>org.springframework.boot</groupId>
  26. <artifactId>spring-boot-starter-test</artifactId>
  27. <scope>test</scope>
  28. <exclusions>
  29. <exclusion>
  30. <groupId>org.junit.vintage</groupId>
  31. <artifactId>junit-vintage-engine</artifactId>
  32. </exclusion>
  33. </exclusions>
  34. </dependency>
  35. </dependencies>
  36. <build>
  37. <plugins>
  38. <plugin>
  39. <groupId>org.springframework.boot</groupId>
  40. <artifactId>spring-boot-maven-plugin</artifactId>
  41. </plugin>
  42. </plugins>
  43. </build>
  44. </project>

编写一个http接口

  1. 在主程序的同级目录下,新建一个controller包,一定要在同级目录下,否则识别不到
  2. 在包中新建一个HelloController类
  1. @RestController
  2. public class HelloController {
  3. @RequestMapping("/hello")
  4. public String hello(){
  5. return "hello world";
  6. }
  7. }
  1. 编写完毕后,从主程序启动项目,浏览器发起请求,看页面返回;控制台输出了Tomcat访问的端口号

SpringBoot学习笔记01 - 图2

简单几步,就完成了一个web接口的开发,SpringBoot就是这么简单,我们常用它来建立我们的微服务项目

将项目打包成jar包,点击maven的package

SpringBoot学习笔记01 - 图3

SpringBoot学习笔记01 - 图4

如果遇上以上的错误,可以配置打包时,跳过项目运行测试用例

  1. <!--
  2. 在工作中,很多情况下我们打包是不想执行测试用例的
  3. 可能是测试用例不完事,或是测试用例会影响数据库数据
  4. 跳过测试用例执
  5. -->
  6. <plugin>
  7. <groupId>org.apache.maven.plugins</groupId>
  8. <artifactId>maven-surefire-plugin</artifactId>
  9. <version>2.18.1</version>
  10. <configuration>
  11. <skipTests>true</skipTests>
  12. </configuration>
  13. </plugin>

如果打包成功,则会在target目录下生成一个jar包

SpringBoot学习笔记01 - 图5

打包成jar包后,就可以在任何地方运行了

如何更改启动时显示的字符拼成的字母,SpringBoot呢?也就是banner图案

只需一步:到项目下的resources目录下新建一个banner.txt即可,图案可以到:https://www.bootschool.net/ascii这个网站生成,然后拷贝到文件中即可

SpringBoot学习笔记01 - 图6

2.SpringBoot运行原理

上一节中创建的springboot项目,是怎么运行的呢,由于它同时也是一个maven项目,所以可以从pom.xml文件开始探究

pom.xml

父依赖

其实他主要是依赖一个父项目,主要是管理项目的资源过滤及插件

  1. <parent>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-parent</artifactId>
  4. <version>2.3.4.RELEASE</version>
  5. <relativePath/> <!-- lookup parent from repository -->
  6. </parent>

点进去发现还有一个父依赖

  1. <parent>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-dependencies</artifactId>
  4. <version>2.3.4.RELEASE</version>
  5. </parent>

这里才是真正管理springboot应用里面所有依赖版本的地方,springboot的版本控制中心;

以后导入依赖默认是不需要写版本;但是如果导入的包没有在依赖中管理着就需要手动配置版本了

启动器spring-boot-starter

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-web</artifactId>
  4. </dependency>

spring-boot-starter-xxx: 就是springboot的场景启动器

spring-boot-starter-web: 帮我们导入了web模块正常运行所依赖的组件

springboot将所有的功能场景都抽取出来,做成一个个的starter(启动器),只需要在项目中引入这些starter即可,所有相关的依赖都会导入进来,我们用到什么功能就导入什么样的场景启动器即可,未来也可以自己定义starter

主启动类

分析完了pom.xml,来看看这个启动类

默认的主启动类

  1. package com.jcsune;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. //说明这是一个springboot应用
  5. @SpringBootApplication
  6. public class Springboot01Application {
  7. public static void main(String[] args) {
  8. SpringApplication.run(Springboot01Application.class, args);
  9. }
  10. }

但是一个简单的启动类并不简单,我们来分析一下这些注解都干了什么

@SpringBootApplication

作用:标注在某个类上说明这个类是SpringBoot的主配置类,SpringBoot就应该运行这个类的main方法来启动SpringBoot应用

进入这个注解:看到上面还有很多其他注解

  1. @Target({ElementType.TYPE})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Inherited
  5. @SpringBootConfiguration
  6. @EnableAutoConfiguration
  7. @ComponentScan(
  8. excludeFilters = {@Filter(
  9. type = FilterType.CUSTOM,
  10. classes = {TypeExcludeFilter.class}
  11. ), @Filter(
  12. type = FilterType.CUSTOM,
  13. classes = {AutoConfigurationExcludeFilter.class}
  14. )}
  15. )

@ComponentScan

这个注解在spring中很重要,它对应XML配置中的元素

作用:自动扫描并加载符合条件的组件或者bean,将这个bean定义加载到IOC容器中

@SpringBootConfiguration

作用:springboot的配置类,标注在某个类上,表示这是一个springboot的配置类,我们继续进去这个注解查看

  1. // 点进去得到下面的 @Component
  2. @Configuration
  3. public @interface SpringBootConfiguration {}
  4. @Component
  5. public @interface Configuration {}

这里的@Configuration,说明这是一个配置类,配置类就是对应Spring的xml文件

里面的@Component这就说明启动类本身也是Spring中的一个组件而已,负责启动应用!

@EnableAutoConfiguration

@EnableAutoConfiguration:开启自动配置功能

以前我们需要自己配置的东西,而现在springboot可以自动帮我们配置

@EnableAutoConfiguration告诉SpringBoot开启自动配置功能,这样自动配置才能生效

点进注解继续查看

  1. @Target({ElementType.TYPE})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Inherited
  5. @AutoConfigurationPackage
  6. @Import({AutoConfigurationImportSelector.class})
  7. public @interface EnableAutoConfiguration {
  8. String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
  9. Class<?>[] exclude() default {};
  10. String[] excludeName() default {};
  11. }
  12. //继续进入@AutoConfigurationPackage注解
  13. @Target({ElementType.TYPE})
  14. @Retention(RetentionPolicy.RUNTIME)
  15. @Documented
  16. @Inherited
  17. @Import({Registrar.class})
  18. public @interface AutoConfigurationPackage {
  19. String[] basePackages() default {};
  20. Class<?>[] basePackageClasses() default {};
  21. }

@AutoConfigurationpackage:自动配置包

@Import :Spring底层注解@Import,给容器中导入一个组件

Registrar.class作用:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器;

这个分析完了,退到上一步,继续看

@Import({AutoConfigurationImportSelector.class}):给容器导入组件;

AutoConfigurationImportSelector:自动配置导入选择器,那么他会导入哪些组件的选择器呢?点击去这个类看源码

  1. 这个类中有一个这样的方法
  1. // 获得候选的配置
  2. protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
  3. //这里的getSpringFactoriesLoaderFactoryClass()方法
  4. //返回的就是我们最开始看的启动自动导入配置文件的注解类;EnableAutoConfiguration
  5. List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
  6. 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.");
  7. return configurations;
  8. }
  1. 这个方法又调用了SpringFactoriesLoader类的静态方法,我们进入SpringFactoriesLoader类loadFactoryNames()

方法

  1. public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
  2. String factoryClassName = factoryClass.getName();
  3. //这里它又调用了 loadSpringFactories 方法
  4. return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
  5. }
  1. 我们继续点击查看loadSpringFactories方法
  1. private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
  2. //获得classLoader , 我们返回可以看到这里得到的就是EnableAutoConfiguration标注的类本身
  3. MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
  4. if (result != null) {
  5. return result;
  6. } else {
  7. try {
  8. //去获取一个资源 "META-INF/spring.factories"
  9. Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
  10. LinkedMultiValueMap result = new LinkedMultiValueMap();
  11. //将读取到的资源遍历,封装成为一个Properties
  12. while(urls.hasMoreElements()) {
  13. URL url = (URL)urls.nextElement();
  14. UrlResource resource = new UrlResource(url);
  15. Properties properties = PropertiesLoaderUtils.loadProperties(resource);
  16. Iterator var6 = properties.entrySet().iterator();
  17. while(var6.hasNext()) {
  18. Entry<?, ?> entry = (Entry)var6.next();
  19. String factoryClassName = ((String)entry.getKey()).trim();
  20. String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
  21. int var10 = var9.length;
  22. for(int var11 = 0; var11 < var10; ++var11) {
  23. String factoryName = var9[var11];
  24. result.add(factoryClassName, factoryName.trim());
  25. }
  26. }
  27. }
  28. cache.put(classLoader, result);
  29. return result;
  30. } catch (IOException var13) {
  31. throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
  32. }
  33. }
  34. }
  1. 发现一个多次出现的文件:spring factories,全局搜索他

spring.factories

根据源头打开spring.factories,看到了很多自动配置的文件;这就是自动配置根源所在

SpringBoot学习笔记01 - 图7

自动配置真正实现的是从classpath中搜寻所有的META-INF/Spring.factories配置文件,并将其中对应的org.springframework.boot.autoconfigure.包下的配置项,通过反射实例化为对应标注了@Configuration的JavaConfig形式的IOC容器配置类,然后将这些都汇总成为一个实例并加载到IOC容器中

结论

  1. SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值
  2. 将这些值作为自动配置类导入容器,自动配置类就生效,帮我们进行自动配置工作
  3. 整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中
  4. 它会给容器中导入非常多的自动配置类(xxxAutoConfiguration),就是给容器中导入这个场景需要的所有组件,并配置好这些组件
  5. 有了自动配置类,免去了我们手动编写配置注入功能组件等的工作

SpringApplication

  1. @SpringBootApplication
  2. public class SpringbootApplication {
  3. public static void main(String[] args) {
  4. SpringApplication.run(SpringbootApplication.class, args);
  5. }
  6. }

SpringApplication.run分析

分析该方法主要分两部分,一部分是SpringApplication的实例化,二是run方法的执行;

SpringApplication

这个类主要做了以下四件事情:

  1. 推断应用的类型是普通的项目还是Web项目
  2. 查找并加载所有可用初始化器,设置到initializers属性中
  3. 找到所有的应用程序监听器,设置到listeners属性中
  4. 推断并设置main方法的定义类,找到运行的主类

查看构造器:

  1. public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
  2. // ......
  3. this.webApplicationType = WebApplicationType.deduceFromClasspath();
  4. this.setInitializers(this.getSpringFactoriesInstances();
  5. this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
  6. this.mainApplicationClass = this.deduceMainApplicationClass();
  7. }

3.yaml配置注入

3.1 yaml语法学习

配置文件

SpringBoot使用一个全局的配置文件,配置文件名称是固定的

  • application.properties
    • 语法结构:key=value
  • application.yml
    • 语法结构:key: 空格 value

配置文件的作用:修改SpringBoot自动配置的默认值,因为SpringBoot在底层都给我们自动配置好了

比如我们可以在配置文件中修改Tomcat默认启动的端口号

  1. server.port=8081

yaml概述

YAML是”YAML Ain’t a Markup Language” (YAML不是一种标记语言)的递归缩写,在开发这种语言时,YAML的意思其实是:“Yet Another Markup Language”(仍是一种标记语言)

这种语言以数据作为中心,而不是以标记语言为重点

以前的配置文件,大多数都是使用xml来配置;比如一个简单的端口设置,我们来对比一下yaml和xml

传统xml配置:

  1. <server>
  2. <port>8081</port>
  3. </server>

yaml配置:

  1. server:
  2. prot: 8080

yaml基础语法

说明:语法要求严格

  1. 空格不能省略
  2. 以缩进来控制层级关系,只要是左边对齐的一列数据都是同一个层级的
  3. 属性和值得大小写都是十分敏感的

字面值: 普通的值[数字,布尔值,字符串]

字面量直接写在后面就可以,字符串默认不用加上双引号或者单引号;

  1. k: v

注意:

  • “ “双引号,不会转义字符串里面的特殊字符,特殊字符会作为本身想表示的意思

比如:name: “kuang \n shen” 输出:kuang 换行 shen

  • ‘’ 单引号,会转义特殊字符,特殊字符最终会变成和普通字符一样输出

比如:name: ‘kuang \n shen’ 输出:kuang \n shen

对象、Map(键值对)

  1. #对象、Map格式
  2. k:
  3. v1:
  4. v2:

在下一行来写对象的属性和值得关系,注意缩进;比如

  1. student:
  2. name: jcsune
  3. age: 3

行内写法

  1. student: {name: jcsune,age: 3}

数组(List、set)

用-值表示数组中的一个元素,比如:

  1. pets:
  2. - cat
  3. - dog
  4. - pig

行内写法

  1. pets: [cat,dog,pig]

修改SpringBoot的默认端口号

配置文件中添加端口号的参数,就可以切换端口;

  1. server:
  2. port: 8082

3.2 注入配置文件

yaml文件更强大的地方在于他可以给我们的实体类直接注入匹配值

yaml注入配置文件

  1. 在springboot-02项目中的resources目录下新建一个文件application.yml
  2. 编写一个实体类Dog
  1. package com.jcsune.springboot02.pojo;
  2. import lombok.Data;
  3. import org.springframework.stereotype.Component;
  4. @Data
  5. @Component //注册bean到容器中
  6. public class Dog {
  7. private String name;
  8. private Integer age;
  9. }
  1. 思考,我们原来是如何给bean注入属性值的!@Value, 给Dog类测试一下
  1. @Component //注册bean到容器中
  2. public class Dog {
  3. @Value("旺财")
  4. private String name;
  5. @Value("3")
  6. private Integer age;
  7. }
  1. 在SpringBoot的测试类下注入测试一下
  1. @SpringBootTest
  2. class Springboot02ApplicationTests {
  3. //将狗狗自动注入进来
  4. @Autowired
  5. Dog dog;
  6. @Test
  7. void contextLoads() {
  8. //打印看下狗狗对象
  9. System.out.println(dog);
  10. }
  11. }

结果成功输出,@Value注入成功,这是我们原来的方法

SpringBoot学习笔记01 - 图8

  1. 我们在编写一个复杂一点的实体类:Person类
  1. //注册bean到容器中
  2. @Component
  3. @Data
  4. @AllArgsConstructor
  5. @NoArgsConstructor
  6. public class Person {
  7. private String name;
  8. private Integer age;
  9. private Boolean happy;
  10. private Date birthday;
  11. private Map<String,Object> maps;
  12. private List<Object> lists;
  13. private Dog dog;
  14. }
  1. 我们来使用yaml配置的方式来进行注入。写的时候注意区别和优势,我们来编写一个yaml配置!
  1. person:
  2. name: jcsune
  3. age: 18
  4. happy: yes
  5. birth: 1997/06/28
  6. maps: {k1: v1,k2: v2}
  7. lists:
  8. - code
  9. - girl
  10. - music
  11. dog:
  12. name: 旺财
  13. age: 1
  1. 刚才已经把person这个对象的所有值都写好了,现在就注入到我们的类中
  1. //注册bean到容器中
  2. @Component
  3. @Data
  4. @AllArgsConstructor
  5. @NoArgsConstructor
  6. @ConfigurationProperties(prefix = "peron")
  7. public class Person {
  8. private String name;
  9. private Integer age;
  10. private Boolean happy;
  11. private Date birthday;
  12. private Map<String,Object> maps;
  13. private List<Object> lists;
  14. private Dog dog;
  15. }
  1. 此时我们还需要添加一个依赖
  1. <!-- 导入配置文件处理器,配置文件进行绑定就会有提示,需要重启 -->
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-configuration-processor</artifactId>
  5. <optional>true</optional>
  6. </dependency>
  1. 确认以上配置都OK后,去测试类中测试一下
  1. @Autowired
  2. Person person;//将person自动注入进来
  3. @Test
  4. void contextLoads() {
  5. //打印看下狗狗对象
  6. System.out.println(person);//打印person信息
  7. }

结果:所有值全部注入成功

SpringBoot学习笔记01 - 图9

至此,yaml配置注入到实体类完全OK