# 一、Spring与SpringBoot
1. Spring生态
Springboot+Spring Framework+Spring Data+Spring Cloud+Spring Security…
覆盖了:
- web开发、数据访问、安全控制、分布式、消息服务、移动开发、批处理…
Spring5重大升级:
- 响应式编程:占用少量资源来处理大规模并发
- 内部源码设计改变
适配器设计模式:A接口有(1,2,3,4,5方法),B适配器实现A。C类继承B(重写1,2,3方法),D类继承B重写(4,5方法)
基于Java8的一些新特性,如:提供了接口默认实现。Spring5重新设计源码架构。
2. SpringBoot特点
特点:能快速创建出生产级别的Spring应用,整合Spring其他框架
2.1 优点
-
创建独立Spring应用
-
Embed Tomcat, Jetty or Undertow directly (no need to deploy WAR files)
-
Provide opinionated ‘starter’ dependencies to simplify your build configuration
- 自动starter依赖,简化构建配置
-
Automatically configure Spring and 3rd party libraries whenever possible
- 自动配置Spring以及第三方功能
-
提供生产级别的监控、健康检查及外部化配置
-
无代码生成、无需编写XML
-
SpringBoot是整合Spring技术栈的一站式框架
-
SpringBoot是简化Spring技术栈的快速开发脚手架
2.2 缺点
- 人称版本帝,迭代快,需要时刻关注变化
- 封装太深,内部原理复杂,不容易精通
3. SpringBoot时代背景
3.1 微服务
- 微服务是一种架构风格,将大型软件拆分为小服务独立部署
- 一个应用拆分为一组小型服务
- 每个服务运行在自己的进程内,也就是可独立部署和升级
- 服务之间使用轻量级HTTP交互
- 服务围绕业务功能拆分
- 可以由全自动部署机制独立部署
- 去中心化,服务自治。服务可以使用不同的语言、不同的存储技术。
3.2 分布式
分布式的困难:
- 远程调用:各个服务之间通过http交互
- 服务发现:相同服务被部署在不同机器上,需要发现正常运行服务
- 负载均衡:相同服务被部署在不同机器上,选择调用哪个
- 服务容错:服务出现异常时处理
- 配置管理:所有服务配置放在配置中心中,只需修改一处
- 服务监控:监控服务健康状况,cpu占用等
- 链路追踪:追踪服务间互相调用
- 日志管理:分布式日志管理
- 任务调度:并行还是串行,或者某一个先触发
- …
分布式的解决:
- SpringBoot(开发各种微服务应用)+ SpringCloud(整合)
3.3 云原生
即原生应用如何上云。 Cloud Native
上云的困难:
- 服务自愈:C服务部署两台服务器,有一天一台炸了后,在其他服务器拉起一份C
- 弹性伸缩:流量高峰时,C服务能自动扩充几台;高峰过后自动下线
- 服务隔离:同台服务器上部署C、D、E服务,互不影响
- 自动化部署:将微服务部署到云平台
- 灰度发布:B服务更新2.0后,将某几台机子换成新版本,经过长时间迭代后发现没问题再把老版本逐一下线
- 流量治理:限制每台服务器的流量
- …
上云的解决:
4. SpringBoot官方文档
-
官方文档架构
二、Springboot2入门
- Maven配置文件
新添内容:
<mirrors>
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>central</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
</mirrors>
<profiles>
<profile>
<id>jdk-1.8</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>1.8</jdk>
</activation>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
</properties>
</profile>
</profiles>
需求:浏览发送/hello请求,响应 “Hello,Spring Boot 2”
1.创建maven工程
2.引入依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
3.创建主程序
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
}
4.编写业务
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@RequestMapping("/hello")
public String handle01(){
return "Hello, Spring Boot 2!";
}
}
5.运行&测试
- 运行
MainApplication
类 - 浏览器输入
http://localhost:8080/hello
,将会输出Hello, Spring Boot 2!
- 打印日志
- @slf4j注解类
- log.info(“要检测的内容:username={}”,username);
设置配置:
maven工程的resource文件夹中创建application.properties文件。
# 设置端口号
server.port=8888
6.打包部署
在pom.xml添加:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
- 在IDEA的Maven插件上点击运行 clean 、package,把helloworld工程项目的打包成jar包,
打包好的jar包被生成在helloworld工程项目的target文件夹内。
-
用cmd运行
java -jar boot-01-helloworld-1.0-SNAPSHOT.jar
,既可以运行helloworld工程项目。 -
将jar包直接在目标服务器执行即可。
三、了解自动配置原理
1. 依赖管理
a> 父项目做依赖管理,后续引入依赖时不需要加版本号
依赖管理:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
</parent>
源码中父项目如下:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.4.RELEASE</version>
</parent>
它几乎声明了所有开发中常用的依赖的默认版本号,自动版本仲裁机制
b> 开发导入 spring-boot-starter-场景启动器
- 只要引入starter,这个场景的所有常规需要的依赖我们都自动引入,如
spring-boot-starter-web
- 更多SpringBoot所有支持的场景;见到的 *-spring-boot-starter: 第三方为我们提供的简化开发的场景启动器。
所有场景启动器最底层的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.3.4.RELEASE</version>
<scope>compile</scope>
</dependency>
c> 导入依赖时无需关注版本号,自动版本仲裁
- 引入依赖默认都可以不写版本
- 引入非版本仲裁的jar,要写版本号。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
d> 可以修改默认版本号
- 查看spring-boot-dependencies里面规定当前依赖的版本用的 key。
- 在当前项目里面重写配置pom.xml,如下面的代码。
<properties>
<mysql.version>8.0.26</mysql.version>
</properties>
2. 自动配置
组件在容器中有,就会生效:
public static void main(String[] args) {
//1、返回我们IOC容器
ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
//2、查看容器里面的组件
String[] names = run.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
}
- 自动配好Tomcat (由spring-boot-starter-web引入)
- 引入Tomcat依赖。
- 配置Tomcat
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.3.4.RELEASE</version>
<scope>compile</scope>
</dependency>
-
自动配好SpringMVC(由spring-boot-starter-web引入)
- 引入SpringMVC全套组件
- 自动配好SpringMVC常用组件(DispatcherServlet,ViewResolver, multipartResolver)
-
自动配好Web常见功能,如:字符编码问题
- SpringBoot帮我们配置好了所有web开发的常见场景
-
默认的包结构
- 无需以前的包扫描配置,主程序所在包及其下面的所有子包里面的组件都会被默认扫描进来
- 想要改变扫描路径
- @SpringBootApplication(scanBasePackages=“com.zju”)
- @ComponentScan 指定扫描路径
@SpringBootApplication
==等同于==
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.zju.boot")
- 各种配置拥有默认值
- 配置文件application.properties的值最终会绑定每个类上,如:
MultipartProperties
这个类会在容器中创建对象 - 想要修改配置只需要在配置文件中设置
- 配置文件application.properties的值最终会绑定每个类上,如:
- 按需加载所有自动配置项
- SpringBoot所有的自动配置功能都在 spring-boot-autoconfigure 包里面
- 有非常多的starter,引入了哪些场景这个场景的自动配置才会开启
- …
3. 容器功能
3.1 组件添加
@Configuration
组件的循环依赖特性,必须是单实例
-
配置类本身也是组件
-
配置类里面使用@Bean标注在方法上给容器注册组件,默认也是单实例的
-
Full模式(proxyBeanMethods = true):保证每个@Bean方法被调用多少次返回的组件都是单实例的
- 配置 类组件之间有依赖关系,方法会被调用得到之前单实例组件,用Full模式(默认)
-
Lite模式(proxyBeanMethods = false):每个@Bean方法被调用多少次返回的组件都是新创建的
- 配置 类组件之间无依赖关系用Lite模式加速容器启动过程,减少判断
@Configuration(proxyBeanMethods = false) //告诉SpringBoot这是一个配置类 == 配置文件
public class MyConfig {
/**
* Full:外部无论对配置类中的这个组件注册方法调用多少次获取的都是之前注册容器中的单实例对象
* @return
*/
@Bean //给容器中添加组件。以方法名作为组件的id。返回类型就是组件类型。返回的值,就是组件在容器中的实例
public User user01(){
User zhangsan = new User("zhangsan", 18);
//user组件依赖了Pet组件
zhangsan.setPet(tomcatPet());
return zhangsan;
}
@Bean("tom")
public Pet tomcatPet(){
return new Pet("tomcat");
}
}
//Tomcat中组件为
Spring中基本标签
它们是Spring的基本标签,在Spring Boot中并未改变它们原来的功能
-
@Bean:表示方法返回对象,然后对象交给IOC管理
-
@Component:普通的注解
-
@Controller:web层
-
@Service:业务逻辑层以及Service层
-
@Repository:dao层即持久层
-
@ComponentScan:扫描包
@Import
@Import({User.class, DBHelper.class,…})自动从类中的无参构造函数创建一个实例注册到IOC容器中
【注意】IOC容器中默认组件的名字id就是全类名,如 User 类就是:com.zju.boot.bean.User
@Import({User.class, DBHelper.class})
public class MyConfig {
}
@Conditional
条件装配:满足Conditional指定的条件,则进行组件注入
用@ConditionalOnMissingBean举例说明:
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(name = "tom")//没有tom名字的Bean时,MyConfig类的Bean才能生效。
public class MyConfig {
@Bean
public User user01(){
User zhangsan = new User("zhangsan", 18);
zhangsan.setPet(tomcatPet());
return zhangsan;
}
@Bean("tom22")
public Pet tomcatPet(){
return new Pet("tomcat");
}
}
public static void main(String[] args) {
//1、返回我们IOC容器
ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
boolean tom = run.containsBean("tom");
System.out.println("容器中Tom组件:"+tom);//false
boolean user01 = run.containsBean("user01");
System.out.println("容器中user01组件:"+user01);//true
boolean tom22 = run.containsBean("tom22");
System.out.println("容器中tom22组件:"+tom22);//true
}
3.2 原生配置文件引入
@ImportResource导入Spring配置文件
使用场景:比如,公司使用bean.xml文件生成配置bean,然而你为了省事,想继续复用bean.xml,@ImportResource粉墨登场。
beans.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans ...">
<bean id="haha" class="com.lun.boot.bean.User">
<property name="name" value="zhangsan"></property>
<property name="age" value="18"></property>
</bean>
<bean id="hehe" class="com.lun.boot.bean.Pet">
<property name="name" value="tomcat"></property>
</bean>
</beans>
使用方法:将注解加到配置类中
@ImportResource("classpath:beans.xml")
public class MyConfig {
...
}
- classpath:
spring.banner.image.location=classpath:banner.jpg
classpath:只会到你的class路径中查找文件
3.3 属性值绑定
读取properties文件中的内容,并且把它封装到JavaBean中,以供随时使用
方法一:
@ConfigurationProperties + @Component
在实体类中配置@ConfigurationProperties
配置文件application.properties中
mycar.brand=BYD
mycar.price=100000
//只有在容器中的组件,才会拥有SpringBoot提供的强大功能
@Component
@ConfigurationProperties(prefix = "mycar")
public class Car {
private String brand;
private Integer price;
...
}
@RestController
public class HelloController {
@Autowired
Car car;//根据类型属性注入
@RequestMapping("/car")
public Car car(){
return car;
}
}
方法二:
@EnableConfigurationProperties + @ConfigurationProperties
应用场景,引用第三方包中类没有配置@Component注解
-
开启Car属性配置绑定功能
-
把这个Car这个组件自动注册到容器中
@EnableConfigurationProperties(Car.class)
public class MyConfig {
...
}
@ConfigurationProperties(prefix = "mycar")
public class Car {
...
}
4. 自动装配原理入门
4.1 引导加载自动配置类
Spring Boot应用启动类:
@SpringBootApplication
=@SpringBootConfiguration
+@EnableAutoConfiguration
+@ComponentScan
@SpringBootConfiguration
@Configuration
代表当前是一个配置类。
@ComponentScan
指定要扫描哪些包
@EnableAutoConfiguration
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
...
}
- @AutoConfigurationPackage自动包规则原理
自动配置包?指定了默认的包规则,即mainApplication所在包路径
@Import(AutoConfigurationPackages.Registrar.class)//给容器中导入一个组件
public @interface AutoConfigurationPackage {
...
}
- 利用Registrar给容器中批量导入一系列组件
- 将指定的一个包下的所有组件导入进MainApplication所在包下,解释了SpringBoot在mainApplication所在包下类中注解能被识别
- @Import(AutoConfigurationImportSelector.class)初始加载自动配置类
- 利用
getAutoConfigurationEntry(annotationMetadata);
给容器中批量导入一些组件 - 调用
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes)
获取到所有需要导入到容器中的配置类 - 利用工厂加载
Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader);
得到所有的组件 - 从
META-INF/spring.factories
位置来加载一个文件。- 默认扫描我们当前系统里面所有
META-INF/spring.factories
位置的文件 spring-boot-autoconfigure-2.3.4.RELEASE.jar
包里面也有META-INF/spring.factories
- 默认扫描我们当前系统里面所有
# 文件里面写死了spring-boot一启动就要给容器中加载的所有配置类127个
# spring-boot-autoconfigure-2.3.4.RELEASE.jar/META-INF/spring.factories
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
...
4.2 按需开启自动配置项
虽然我们127个场景的所有自动配置类启动的时候默认全部加载,
但是xxxxAutoConfiguration
按照条件装配规则(@Conditional
),最终会按需配置
如
AopAutoConfiguration
类:
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnProperty(
prefix = "spring.aop",
name = "auto",
havingValue = "true",
matchIfMissing = true
)
public class AopAutoConfiguration {
public AopAutoConfiguration() {
}
...
}
4.3 定制化修改自动配置
以autoconfigure—web—servlet—DispatcherServletAutoConfiguration—multipartResolver为例
//文件上传解析器自动配置
@Bean
@ConditionalOnBean(MultipartResolver.class) //容器中有这个类型组件
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) //容器中没有这个名字 multipartResolver 的组件
public MultipartResolver multipartResolver(MultipartResolver resolver) {
//给@Bean标注的方法传入了对象参数,这个参数的值就会从容器中找。此时容器中有MultipartResolver但命名不规范。
//SpringMVC中命名为multipartResolver。防止有些用户配置的文件上传解析器命名不符合规范。
// Detect if the user has created a MultipartResolver but named it incorrectly
return resolver;//给容器中加入了文件上传解析器;
}
- SpringBoot默认会在底层配好所有的组件,但是如果用户自己配置了以用户的优先
以ChracterEncoding为例
4.4 总结自动装配流程
- SpringBoot先加载所有的自动配置类 xxxAutoConfiguration
- 每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值(xxxProperties里面读取,xxxProperties和配置文件进行了绑定)
- 生效的配置类就会给容器中装配很多组件(@Bean)
- 只要容器中有这些组件,相当于这些功能就有了
- 定制化配置(以用户自己配置优先)
- 用户直接自己==@Bean替换底层的组件==
- 用户去看这个组件是获取的配置文件什么值就去application.properties修改配置文件
xxxAutoConfiguration加载自动配置类 —> 装配组件 —> xxxProperties里面拿值属性值 ----> application.properties自定义属性值
因此,修改配置文件即为修改整合场景的配置,配置文档或者查看底层源码
4.5 Springboot开发最佳实践
a> 应用如何编写
- 引入场景依赖
- 参考官方文档,xxx-starter
- 查看自动配置了哪些(选做)
- 配置文件中debug=true开启自动配置报告,可以查看生效配置
- Negative matches(不生效)
- Positive matches(生效)
- 配置文件中debug=true开启自动配置报告,可以查看生效配置
- 是否需要修改配置,改变默认行为
- 参照文档修改配置项
- 参考官方文档 xxxproperties
- 自定义加入或者替换组件
- @Bean、@Component…
- 自定义器 XXXXXCustomizer
- 参照文档修改配置项
b> Lombok简化开发
Lombok用标签方式代替构造器、getter/setter、toString()等鸡肋代码。
-
安装Lombok插件
-
spring boot已经管理Lombok,引入依赖:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
- 在实体类加上注解
@NoArgsConstructor
//@AllArgsConstructor
@Data
@ToString
@EqualsAndHashCode
public class User {
private String name;
private Integer age;
private Pet pet;
public User(String name,Integer age){
this.name = name;
this.age = age;
}
}
- 简化日志开发,可以相当于sout使用
@Slf4j
@RestController
public class HelloController {
@RequestMapping("/Hello/{username}")
public String HelloMapping(@PathVariable("username") String username){
log.info("请求进来了");
return "Hello, wz "+username;
}
}
c> dev-tools
实现热部署,自动重启
- 添加依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
在IDEA中,项目或者页面修改以后:Build project(Ctrl+F9)。
d> Spring Initailizr
Spring Initailizr是创建Spring Boot工程向导,搭建好基础架构
在IDEA中,菜单栏New -> Project -> Spring Initailizr
四、配置文件
1. properties
application.properties中写配置文件
2. yaml
一种标记语言,非常适合用来做以数据为中心的配置文件,替代xml
基本语法
- key: value;kv之间有空格
- 大小写敏感
- 使用缩进表示层级关系
- 缩进不允许使用tab,只允许空格
- 缩进的空格数不重要,只要相同层级的元素左对齐即可
- '#'表示注释
- 字符串无需加引号,如果要加,单引号’'、双引号""表示字符串内容会被 转义、不转义
数据类型
- 字面量:单个的、不可再分的值。date、boolean、string、number、null
k: v
- 对象:键值对的集合。map、hash、object
#行内写法:类似于json
k: {k1: v1,k2: v2,k3: v3}
#或
k:
k1: v1
k2: v2
k3: v3
- 数组:一组按次序排列的值。array、list、queue、set
#行内写法:类似于json
k: [v1,v2,v3]
#或者
k:
- v1
- v2
- v3
实例
@Data
public class Person {
private String userName;
private Boolean boss;
private Date birth;
private Integer age;
private Pet pet;
private String[] interests;
private List<String> animal;
private Map<String, Object> score;
private Set<Double> salarys;
private Map<String, List<Pet>> allPets;
}
@Data
public class Pet {
private String name;
private Double weight;
}
- 用yaml表示以上对象(properties>yml>yaml),配置一般是为了初始化使用的
person:
username: fkd
boss: true
birth: 2022/4/21 21:55:00
age: 23
pet: {name: wz,weight: 23}
interests: [篮球,足球]
animal: [猫,够,兔子]
score: {math: 80, English: 90}
salaries: [32000,43000,31000]
allPets: {sick: [{name: wz,weight: 20},{name: fkd,weight: 22}],healthy:[{name: sk,weight: 30}]}
3. 配置提示
自定义的类和配置文件绑定一般没有提示。若要提示,添加如下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>