SpringBoot学习笔记
Posted new一个对象777
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot学习笔记相关的知识,希望对你有一定的参考价值。
SpringBoot学习
核心思想:约定大于配置!!
you can just run
springboot的主要优点:
- 开箱即用,提供各种配置来简化项目配置
- 基于spring
- 内嵌式容器简化web开发
- 没有冗余代码生成和xml配置的要求
什么是微服务?
微服务是一种风格,原来是all in one(单体架构),完美的阐述了高内聚,低耦合的意思。
即微服务是一种利用分治法的思想,去把一整套非常复杂的业务逻辑给切分成多个简单的业务问题,并采用模块化方法去实现组合的一种架构方法。
它们是相互独立的,这意味着它们可以采用不同的编程语言和数据存储。微服务中几乎不存在集中管理,它使用轻量级的HTTP、REST或Thrift API来进行内部通信。
微服务风格(microservice style)
它用于描述一种设计应用程序的特别方式,作为一套独立可部署的服务。
简而言之,微服务架构风格,就像是把一个单独的应用程序开发为一套小服务,每个小服务运行在自己的进程中,并使用轻量级机制通信,通常是 HTTP API。
整体应用程序(Monolithic applications)相当成功,但是越来越多的人感觉到有点不妥,特别是在云中部署时。变更发布周期被绑定了——只是变更应用程序的一小部分,却要求整个重新构建和部署。随着时间的推移,很难再保持一个好的模块化结构,使得一个模块的变更很难不影响到其它模块。扩展就需要整个应用程序的扩展,而不能进行部分扩展。
这导致了微服务架构风格(microservice architectural style)的出现:把应用程序构建为一套服务。事实是,服务可以独立部署和扩展,每个服务提供了一个坚实的模块边界,甚至不同的服务可以用不同的编程语言编写。它们可以被不同的团队管理。
组件化(Componentization )与服务(Services),组件(component)是一个可独立替换和升级的软件单元。
当寻找把一个大的应用程序拆分成小的部分时,通常管理都会集中在技术层面,UI团队、服务端业务逻辑团队和数据库团队。当使用这种标准对团队进行划分时,甚至小小的更变都将导致跨团队项目协作,从而消耗时间和预算审批。一个高效的团队会针对这种情况进行改善,两权相害取其轻。业务逻辑无处不在。实践中,这就是 Conway’s Law 的一个例子。
设计一个系统的任何组织(广义上)都会产生这样一种设计,其结构是组织交流结构的复制。
——Melvyn Conway, 1967
Melvyn Conway 的意思是,像下图所展示的,设计一个系统时,将人员划分为 UI 团队,中间件团队,DBA 团队,那么相应地,软件系统也就会自然地被划分为 UI 界面,中间件系统,数据库。
微服务(microservice )的划分方法不同,它倾向围绕业务功能的组织来分割服务。这些服务实现商业领域的软件,包括用户界面,持久化存储,任何的外部协作。因此,团队是跨职能的(cross-functional),包含开发过程所要求的所有技能:用户体验(user-experience)、数据库(database)和项目管理(project management)。
Amazon 理念是“你构建,你运维(you build, you run it)”.
Guardian网站就是这方面的一个优秀的例子,它初期被设计和构建成一个整体架构,但它已经向微服务的发展了。整体构架仍然是它网站的核心,但是他们使用微服务来增加那些使用整体架构API的新特性。这种方法增加这些临时的特性非常方便,比如运动新闻的特稿。这样站点的一个部分可以使用快速的开发语言迅速整合起来,当它过时后可以一次性移除。我们发现一家金融机构用相似的方法增加新的市场营销活动,数周或者几个月后把它撤销。
微服务的优缺点?
用springboot的感想
通过springboot进行开发真的很方便,我们只需要新建一个项目,然后新建一个controller别的我们都不需要管,我们事先已经导入了很多依赖,其中就包括springboot内置的tomcat,我们也不需要配置配置文件和tomcat直接run就可以了,就像是我们一开始说的**”you can just run“!!**,springboot相比ssm简化了很多配置,更重要是的springboot是前后端分离的,我们后端写好的项目,打成jar然后前端就直接可以用了,很方便,便于开发。
运行jar的命令“java -jar 包名”。
自动配置原理
pom.xml
- spring-boot-dependencies 核心依赖在父工程中
- 我们在引入一些springboot核心依赖的时候不需要指定版本
启动器:
-
<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> </dependency>
-
如果我们要使用什么功能,我们只需要找到对应的启动器就可以了,即“starter”,因为核心依赖都已经配置好了在父工程里面
-
/**标注这是一个springboot的应用*/ @SpringBootApplication //通过反射启动run方法 使其springboot启动 SpringApplication.run(HelloworldApplication.class, args);
-
@SpringBootConfiguration springboot配置 @Configuration spring配置类 @Component 说明这是spring的组件 @EnableAutoConfiguration 自动配置 @AutoConfigurationPackage 自动配置包 @Import(Registrar.class) 导入自动配置 包注册 @Import(AutoConfigurationImportSelector.class) 自动导入选择
-
META-INF/spring.factories 自动配置的核心文件
拆分来看:
-
@SpringBootApplication
标记这是一个springboot的应用-
@SpringBootConfiguration
核心@Configuration
spring的配置类@Component
说明这是一个spring的组件
-
@EnableAutoConfiguration
核心 自动配置-
@AutoConfigurationPackage
自动配置包@Import(AutoConfigurationPackages.Registrar.class)
自动注册包 自动导包的核心AutoConfigurationPackages.Registrar
-
@Import(AutoConfigurationImportSelector.class)
自动导入选择 选择了什么东西-
getAutoConfigurationEntry
获取自动配置的实体 -
getCandidateConfigurations
获取候选的配置-
protected Class<?> getSpringFactoriesLoaderFactoryClass() return EnableAutoConfiguration.class;
-
-
@ComponentScan(excludeFilters = @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) )
-
-
获取候选位置:
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;
这个文件里面放着所有的自动配置的类,当我们用了某个类的时候 springboot的注解会识别出来你用的是哪个类,然后进一步调用这个文件里面响应的类。
- 核心注解:
@ConditionalOnClass
那么多自动配置,为什么有的类没有生效,必须导入start才生效,这是因为springboot很智能,在他的自动配置的文件里的每一个类都有一个注解,通过判断注解里面的内容是否全部都满足,都满足的话才会生效。爆红,所以不满足。
结论
springboot所有的自动配置都是在启动的时候扫描并加载的,扫描的文件就是上面说的spring.factories
,所有的配置类都在这里面,但是不一定会生效,先要判断条件是否成立,只要导入了对应的start就有对应的启动器了,有了启动器我们的自动装配就会生效,然后就配置成功了!
自动配置真正实现是从classpath中搜寻所有的META-INF/spring.factories配置文件 ,并将其中对应的 org.springframework.boot.autoconfigure. 包下的配置项,通过反射实例化为对应标注了 @Configuration的JavaConfig形式的IOC容器配置类 , 然后将这些都汇总成为一个实例并加载到IOC容器中。
spring.factories全路径名:E:\\Maven\\repository\\org\\springframework\\boot\\spring-boot-autoconfigure\\2.4.3\\spring-boot-autoconfigure-2.4.3.jar!\\META-INF\\spring.factories
步骤:
- springboot启动的时候从类路径下的
\\META-INF\\spring.factories
获取指定的值 - 将这些自动配置的类导入到容器,自动配置类就会生效,帮我们进行自动配置
- 以前我们自动配置的东西 现在springboot帮我们自动配置了
- 整合javaee中所有的自动配置的包都在包
spring-boot-autoconfigure-2.4.3.jar
下 - 他会根据类名把这个组件导入容器,这也是自动装配的最核心的部分
- 容器中也会出现非常多的XXXAutoConfiguration的文件,就是这些类给容器导入了某个场景所需要的所有的组件,@Bean–组件
- 有了自动配置类,免去了我们手动编写配置文件的工作
面试:谈谈你对springboot的理解
- 自动装配
- 主类是怎么运行的,做了哪些事情
- 推断应用的类型是普通的项目还是web项目
- 查找并加载所有的可用初始化容器,设置到initalizers属性中
- 找出所有的应用程序监听器,设置到listeners属性中
- 推断并设置main方法的定义类,找到并运行主类
yaml语法学习
yaml是properties的代替者,功能比properties强大,可以存取数组,对象,字符串,相比propertis更快捷,所以springboot推荐使用yaml,空格很重要。
配置文件
SpringBoot使用一个全局的配置文件 , 配置文件名称是固定的
-
application.properties
-
- 语法结构 :key=value
-
application.yml
-
- 语法结构 :key:空格 value
**配置文件的作用 :**修改SpringBoot自动配置的默认值,因为SpringBoot在底层都给我们自动配置好了;
比如我们可以在配置文件中修改Tomcat 默认启动的端口号!测试一下!
server.port=8081
YAML是 “YAML Ain’t a Markup Language” (YAML不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:“Yet Another Markup Language”(仍是一种标记语言)
这种语言以数据作为中心,而不是以标记语言为重点!
以前的配置文件,大多数都是使用xml来配置;比如一个简单的端口配置,我们来对比下yaml和xml
传统xml配置:
<server>
<port>8081<port>
</server>
yaml配置:
server:
prot: 8080
yaml基础语法
说明:语法要求严格!
1、空格不能省略
2、以缩进来控制层级关系,只要是左边对齐的一列数据都是同一个层级的。
3、属性和值的大小写都是十分敏感的。
字面量:普通的值 [ 数字,布尔值,字符串 ]
字面量直接写在后面就可以 , 字符串默认不用加上双引号或者单引号;
注意:
-
“ ” 双引号,不会转义字符串里面的特殊字符 , 特殊字符会作为本身想表示的意思;
比如 :name: “kuang \\n shen” 输出 :kuang 换行 shen
-
‘’ 单引号,会转义特殊字符 , 特殊字符最终会变成和普通字符一样输出
比如 :name: ‘kuang \\n shen’ 输出 :kuang \\n shen
#key: value
name: qin
# 数组
arrays: [one,two,three]
arrays:
- one
- two
- three
# 对象
student:
name: wang
age: nv
注入配置文件
yaml文件更强大的地方在于,他可以给我们的实体类直接注入匹配值!
首先来一个实例,用狗狗测试一下
@Data
@NoArgsConstructor
@AllArgsConstructor
@Component
public class Dog
@Value("旺财") //通过注解注入内容
private String name;
@Value("3")
private Integer age;
然后我们测试一下
@SpringBootTest
class HelloworldApplicationTests
@Autowired
private Dog dog;
@Test
void contextLoads()
System.out.println(dog);
但是当我们编写一个很复杂类的时候这样做显然很麻烦,所以我们用yaml注入的方式来解决。
首先我们需要一个实体类就叫person吧
/*
@ConfigurationProperties作用:
将配置文件中配置的每一个属性的值,映射到这个组件中;
告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定
参数 prefix = “person” : 将配置文件中的person下面的所有属性一一对应
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Component
@ConfigurationProperties(prefix = "person")
@PropertySource(value = "classpath:kuang.properties")
public class Person
private String name;
private Integer age;
private Boolean happy;
private Date birthday;
private Map<String,Object> maps;
private List<Object> list;
private Dog dog;
然后我们需要一个application.yaml
文件
person:
name: wanghu
age: 13
happy: true
birthday: 2020/05/20
maps: k1: v1,k2: v2
list:
- code
- music
- gril
dog:
name: kuanghua
age: 3
我们在person实体类中已经实现了匹配了,直接运行就可以了。
我们还可以加载指定的配置文件:@PropertySource(value = "classpath:kuang.properties")
。
@PropertySource :加载指定的配置文件;
@configurationProperties:默认从全局配置文件中获取值;
配置文件占位符
配置文件还可以编写占位符生成随机数
person:
name: qinjiang$random.uuid # 随机uuid
age: $random.int # 随机int
happy: false
birth: 2000/01/01
maps: k1: v1,k2: v2
lists:
- code
- girl
- music
dog:
name: $person.hello:other_旺财
age: 1
我们上面采用的yaml方法都是最简单的方式,开发中最常用的;也是springboot所推荐的!那我们来唠唠其他的实现方式,道理都是相同的;写还是那样写;配置文件除了yml还有我们之前常用的properties , 我们没有讲,我们来唠唠!
【注意】properties配置文件在写中文的时候,会有乱码 , 我们需要去IDEA中设置编码格式为UTF-8;
settings–>FileEncodings 中配置;
测试步骤:
1、新建一个实体类User
@Component //注册bean
public class User
private String name;
private int age;
private String sex;
2、编辑配置文件 user.properties
user1.name=kuangshen
user1.age=18
user1.sex=男
3、我们在User类上使用@Value来进行注入!
@Component //注册bean
@PropertySource(value = "classpath:user.properties")
public class User
//直接使用@value
@Value("$user.name") //从配置文件中取值
private String name;
@Value("#9*2") // #SPEL Spring表达式
private int age;
@Value("男") // 字面量
private String sex;
4、Springboot测试
@SpringBootTest
class DemoApplicationTests
@Autowired
User user;
@Test
public void contextLoads()
System.out.println(user);
@Value这个使用起来并不友好!我们需要为每个属性单独注解赋值,比较麻烦;我们来看个功能对比图
1、@ConfigurationProperties只需要写一次即可 , @Value则需要每个字段都添加
2、松散绑定:这个什么意思呢? 比如我的yml中写的last-name,这个和lastName是一样的, - 后面跟着的字母默认是大写的。这就是松散绑定。可以测试一下
3、JSR303数据校验 , 这个就是我们可以在字段是增加一层过滤器验证 , 可以保证数据的合法性
4、复杂类型封装,yml中可以封装对象 , 使用value就不支持
结论:
配置yml和配置properties都可以获取到值 , 强烈推荐 yml;
如果我们在某个业务中,只需要获取配置文件中的某个值,可以使用一下 @value;
如果说,我们专门编写了一个JavaBean来和配置文件进行一一映射,就直接@configurationProperties,不要犹豫!
JSR303数据校验
Springboot中可以用@validated来校验数据,如果数据异常则会统一抛出异常,方便异常中心统一处理。我们这里来写个注解让我们的name只能支持Email格式;
@Component //注册bean
@ConfigurationProperties(prefix = "person")
@Validated //数据校验
public class Person
@Email(message="邮箱格式错误") //name必须是邮箱格式
private String name;
常见参数
@NotNull(message="名字不能为空")
private String userName;
@Max(value=120,message="年龄最大不能查过120")
private int age;
@Email(message="邮箱格式错误")
private String email;
空检查
@Null 验证对象是否为null
@NotNull 验证对象是否不为null, 无法查检长度为0的字符串
@NotBlank 检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格.
@NotEmpty 检查约束元素是否为NULL或者是EMPTY.
Booelan检查
@AssertTrue 验证 Boolean 对象是否为 true
@AssertFalse 验证 Boolean 对象是否为 false
长度检查
@Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内
@Length(min=, max=) string is between min and max included.
日期检查
@Past 验证 Date 和 Calendar 对象是否在当前时间之前
@Future 验证 Date 和 Calendar 对象是否在当前时间之后
@Pattern 验证 String 对象是否符合正则表达式的规则
.......等等
除此以外,我们还可以自定义一些数据校验规则
多环境切换
springboot支持多环境随时切换,为什么要切换环境呢?因为你有可能是开发环境,也有可能是测试环境,所以我们要随时切换。
我们在主配置文件编写的时候,文件名可以是 application-profile.properties/yml , 用来指定多个环境版本;
例如:
application-test.properties 代表测试环境配置
application-dev.properties 代表开发环境配置
但是Springboot并不会直接启动这些配置文件,它默认使用application.properties主配置文件;
我们需要通过一个配置来选择需要激活的环境:
#比如在配置文件中指定使用dev环境,我们可以通过设置不同的端口号进行测试;
#我们启动SpringBoot,就可以看到已经切换到dev下的配置了;
spring.profiles.active=dev
server :
port: 8081
spring:
profiles:
active: dev
---
server:
port: 8082
spring:
profiles: dev
---
server:
port: 8083
spring:
profiles: test
注意:如果yml和properties同时都配置了端口,并且没有激活其他环境 , 默认会使用properties配置文件的!
配置文件加载位置
外部加载配置文件的方式十分多,我们选择最常用的即可,在开发的资源文件中进行配置!
官方外部配置文件说明参考文档
springboot 启动会扫描以下位置的application.properties或者application.yml文件作为Spring boot的默认配置文件:
优先级1:项目路径下的config文件夹配置文件
优先级2:项目路径下配置文件
优先级3:资源路径下的config文件夹配置文件
优先级4:资源路径下配置文件
优先级由高到底,高优先级的配置会覆盖低优先级的配置;
再谈自动装配
配置文件application.yaml
到底能写什么?该怎么写呢?
我们以**HttpEncodingAutoConfiguration(Http编码自动配置)**为例解释自动配置原理;
//表示这是一个配置类 也可以给容器添加组件
@Configuration(proxyBeanMethods = false)
//启动指定类的ConfigurationProperties功能;
//进入这个ServerProperties查看,将配置文件中对应的值和ServerProperties绑定起来;
//并把ServerProperties加入到ioc容器中
@EnableConfigurationProperties(ServerProperties.class)
//Spring底层@Conditional注解
//根据不同的条件判断,如果满足指定的条件,整个配置类里面的配置就会生效;
//这里的意思就是判断当前应用是否是web应用,如果是,当前配置类生效
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
//判断当前项目有没有这个类CharacterEncodingFilter;SpringMVC中进行乱码解决的过滤器;
@ConditionalOnClass(CharacterEncodingFilter.class)
//判断配置文件中是否存在某个配置:server.servlet.encoding.enabled;
//如果不存在,判断也是成立的
//即使我们配置文件中不配置 server.servlet.encoding.enabled=true,也是默认生效的;
@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration
//他已经和SpringBoot的配置文件映射了
private final Encoding properties;
//只有一个有参构造器的情况下,参数的值就会从容器中拿
public HttpEncodingAutoConfiguration(ServerProperties properties)
this.properties = properties.getServlet().getEncoding();
//给容器中添加一个组件,这个组件的某些值需要从properties中获取
@Bean
@ConditionalOnMissingBean //判断容器没有这个组件?
public CharacterEncodingFilter characterEncodingFilter()
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(Encoding.Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(Encoding.Type.RESPONSE));
return filter;
一句话总结:我们在application.yaml
中配置的内容对应的就是我们的spring.factories
文件中定义好的类的属性和方法,且都是通过注解@ConfigurationProperties(prefix = "spring.data.mongodb")
来实现的,我们在配置文件中配置的内容最后就是由此注解调用。
根据当前不同的条件判断,决定这个配置类是否生效!
- 一但这个配置类生效;这个配置类就会给容器中添加各种组件;
- 这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的;
- 所有在配置文件中能配置的属性都是在xxxxProperties类中封装着;
- 配置文件能配置什么就可以参照某个功能对应的这个属性类
- 在配置文件中如何绑定就取决于那个类的注解是如何写的
@ConfigurationProperties(prefix = "spring.redis")
这就是自动装配的原理!
在这里贴一个我认为的比较容易理解的过程:
- SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值
- 将这些值作为自动配置类导入容器 , 自动配置类就生效 , 帮我们进行自动配置工作;
- 以前我们需要自己配置的东西 , 自动配置类都帮我们解决了
- 整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中;
- 它将所有需要导入的组件以全类名的方式返回 , 这些组件就会被添加到容器中 ;
- 它会给容器中导入非常多的自动配置类 (xxxAutoConfiguration), 就是给容器中导入这个场景需要的所有组件 , 并配置好这些组件 ;
- 有了自动配置类 , 免去了我们手动编写配置注入功能组件等的工作;
精髓
1、SpringBoot启动会加载大量的自动配置类
2、我们看我们需要的功能有没有在SpringBoot默认写好的自动配置类当中;
3、我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件存在在其中,我们就不需要再手动配置了)
4、给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们只需要在配置文件中指定这些属性的值即可;
**xxxxAutoConfigurartion:自动配置类;**给容器中添加组件
xxxxProperties:封装配置文件中相关属性;
了解:@Conditional
了解完自动装配的原理后,我们来关注一个细节问题,自动配置类必须在一定的条件下才能生效;
@Conditional派生注解(Spring注解版原生的@Conditional作用)
作用:必须是@
以上是关于SpringBoot学习笔记的主要内容,如果未能解决你的问题,请参考以下文章