【狂神说:笔记】SpringBoot(一:入门)
JavaSpringBootSpringBoot入门狂神说秦疆
SpringBoot入门
一、了解SpringBoot
1.1、什么是SpringBoot
Spring Boot 基于 Spring 开发,Spirng Boot 本身并不提供 Spring 框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于 Spring 框架的应用程序。也就是说,它并不是用来替代 Spring 的解决方案,而是和 Spring 框架紧密结合用于提升 Spring 开发者体验的工具。Spring Boot 以**约定大于配置的核心思想**,默认帮我们进行了很多设置,多数 Spring Boot 应用只需要很少的 Spring 配置。同时它集成了大量常用的第三方库配置(例如 Redis、MongoDB、Jpa、RabbitMQ、Quartz 等等),Spring Boot 应用中这些第三方库几乎可以零配置的开箱即用。
简单来说就是SpringBoot其实不是什么新的框架,它默认配置了很多框架的使用方式,就像maven整合了所有的jar包,spring boot整合了所有的框架 。
1.2、主要优点
- 为所有Spring开发者更快的入门
- 开箱即用,提供各种默认配置来简化项目配置
- 内嵌式容器简化Web项目
- 没有冗余代码生成和XML配置的要求
二、第一个SpringBoot程序
2.1、SpringBoot项目创建方式
方式一: 使用Spring Initializr 的 Web页面创建项目
将SpringBoot项目打包,打包成功(“Build Success”)会生成对应的jar包,然后调用管理员cmd直接执行打包的jar包,即可完成项目部署(友情提示可以shift+鼠标右键显示在此打开powershell窗口)
方式二:直接使用idea创建
彩蛋:
可以通过https://www.bootschool.net/ascii来更改SpringBoot项目启动时的运行图案:
- 到项目下的 resources 目录下新建一个banner.txt 即可。
三、原理探究
3.1、Pom.xml文件
父依赖
pom.xml文件中,有一个父依赖(也可以说是该pom.xml文件依赖一个父项目),主要是管理项目的资源过滤和插件!
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.7</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
点进去是个名为spring-boot-starter-parent
的pom文件,发现该项目也依赖一个父项目(名为spring-boot-dependencies
)
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.6.7</version>
</parent>
这里才是真正的管理SpringBoot应用里面所有依赖版本的位置,SpringBoot的版本控制中心
之后我们导入依赖默认是不需要写版本号的,但是如果导入的包没有在依赖管理中,就需要我们手动来进行版本的配置了
启动器(spring-boot-starter-xx)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
- spring-boot-starter-xx:spring-boot的场景启动器
- spring-boot-starter-web:帮助我们导入web模块正常运行所依赖的组件
- SpringBoot将所有的功能场景都抽取出来,做成一个个start(启动器),只需要在项目中引入这些start即可,所有的依赖都会导入进来;我们要使用什么功能就导入深夜场景的启动器即可;我们也可以自定义start启动器
3.2、主启动类
默认的主启动类:SpringBootFirstApplication
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootFirstApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootFirstApplication.class, args);
}
}
@SpringBootApplication
作用:标注在哪个类上,就说明这个类是SpringBoot的主配置类,SpringBoot就应该运行这个类的main方法来启动SpringBoot应用
点进注解:@SpringBootApplication==>
//元注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
//
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {}
@SpringBootConfiguration
作用:SpringBoot的配置类,标注在哪个类上,表示的哪个就是SpringBoot的配置类
@SpringBootConfiguration==>(进入@SpringBootConfiguration注解里面)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {}
@Configuration
-
这里的@Configuration,说明这相当于是一个Spring的xml配置文件
@Configuration==>(进入@Configuration注解里面)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {}
@Component
这里的@Component,说明启动类本身也是一个组件,负责启动应用
@EnableAutoConfiguration
-
开启自动配置功能(以前我们需要手动配置,现在SpringBoot可以帮我们自动配置)
-
@EnableAutoConfiguration注解“通知”SpringBoot开启自动配置功能,这样自动配置功能才能生效
@EnableAutoConfiguration==>
@AutoConfigurationPackage(自动配置包)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {}
- @Import:Spring底层注解,给容器中导入一个组件
- @Import({AutoConfigurationImportSelector.class}):给容器导入组件
- AutoConfigurationImportSelector:自动配置导入选择器
AutoConfigurationImportSelector类
1、AutoConfigurationImportSelector类中有一个这样的方法
//获得候选的配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
//这里的getSpringFactoriesLoaderFactoryClass返回的就是我们最开始看的启动自动导入配置文件的注解类:@EnableAutoConfiguration
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
}
2、这个方法调用了SpringFactoriesLoader类的静态方法,我们进入SpringFactoriesLoader类的loadFactoryNames方法
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoader == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
String factoryTypeName = factoryType.getName();
//这里它又调用loadSpringFactories方法
return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
3、我们进入loadSpringFactories方法
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
//获取classloader,我们这里得到的就是@EnableAutoConfiguration标注的类本身
Map<String, List<String>> result = (Map)cache.get(classLoader);
if (result != null) {
return result;
} else {
HashMap result = new HashMap();
try {
//取获取一个资源:"META-INF/spring.factories"
Enumeration urls = classLoader.getResources("META-INF/spring.factories");
//将读取到的资源遍历,封装成为一个Properties
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Entry<?, ?> entry = (Entry)var6.next();
String factoryTypeName = ((String)entry.getKey()).trim();
String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
String[] var10 = factoryImplementationNames;
int var11 = factoryImplementationNames.length;
for(int var12 = 0; var12 < var11; ++var12) {
String factoryImplementationName = var10[var12];
((List)result.computeIfAbsent(factoryTypeName, (key) -> {
return new ArrayList();
})).add(factoryImplementationName.trim());
}
}
}
result.replaceAll((factoryType, implementations) -> {
return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
});
cache.put(classLoader, result);
return result;
} catch (IOException var14) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
}
}
}
4、发现多次出现:spring.factories
@AutoConfigurationPackage==>
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {}
- Registrar.class:将主动类所在包及包下面所有子包里面的所有组件扫描到Spring容器中
3.3、spring.factories
根据源头打开spring.factories,看到很多自动配置的文件,这就是自动配置根源所在!
我们在上面的自动配置类里面随便打开一个,比如:WebMvcAutoConfiguration
可以看到这些都是JavaConfig配置类(就是Spring框架中,可以用Java的方式来配置Spring,代替Spring自带的application.xml配置文件)
所有,自动配置真正实现是从classpath中搜索所有的META-INF/spring.factories配置文件,并将其中对应的 org.springframework.boot.autoconfigure
包下的配置项通过反射实例化成为标注了 @Configuration的JavaConfig形式的IOC容器配置类,然后将这些汇总成为一个实例并加载到IOC容器中。
总结:
- SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值
- 将这些值作为自动配置类导入容器,自动配置类就会生效,帮我们进行自动配置工作
- 整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中
- 它会给容器中导入非常多的自动配置类 (xxxAutoConfiguration), 就是给容器中导入这个场景需要的所有组件 , 并配置好这些组件
- 有了自动配置类 , 免去了我们手动编写配置注入功能组件等的工作
3.4、SpringApplication
不简单的方法
最初以为就运行一个main方法,没想到开启了一个服务
@SpringBootApplication
public class SpringBootFirstApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootFirstApplication.class, args);
}
}
SpringApplication.run分析
- SpringApplication的实例化
- run方法的执行
SpringApplication
这个类主要做了一下四件事:
-
推断应用的类型是普通的项目还是Web项目
-
查找并加载所有可用初始化器 , 设置到initializers属性中
-
找出所有的应用程序监听器,设置到listeners属性中
-
推断并设置main方法的定义类,找到运行的主类
SpringApplication类的构造器:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = Collections.emptySet();
this.isCustomEnvironment = false;
this.lazyInitialization = false;
this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
this.applicationStartup = ApplicationStartup.DEFAULT;
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();
}
3.5、关于SpringBoot,谈谈你的理解?
- 自动装配
- run()(不要讲很深,讲解一下它的作为就可以,4点)
四、SpringBoot配置
4.1、什么是yaml
YAML是"YAML Ain’t a Markup Language"(YAML不是一种标记语言)的递归缩写。在开发的这种语言时,YAML *的意思其实是:“Yet Another Markup Language”(仍是一种标记语言),但为了强调这种语言以数据做为中心,而不是以标记语言为重点,而用反向缩略语重命名。
配置文件:SpringBoot使用一个全局的配置文件,配置文件名称是固定的
-
application.properties
- 语法结构:key=value
-
application.yaml
- 语法结构:key:空格value
配置文件的作用:修改SpringBoot自动配置的默认值,因为SpringBoot在底层都给我们自动配置好了;
4.2、基本语法格式
- 空格
- 以缩进来控制层级关系
- 属性和值的大小写都是十分敏感
- 字面量直接写在后面就可以 , 字符串默认不用加上双引号或者单引号
注意:
-
“ ” 双引号,会转义字符串里面的特殊字符 , 特殊字符会作为本身想表示的意思;
比如 :name: “kuang \n shen” 输出 :kuang 换行 shen
-
‘’ 单引号,会转义特殊字符 , 特殊字符最终会变成和普通字符一样输出
比如 :name: ‘kuang \n shen’ 输出 :kuang \n shen
4.3、.yml文件注入配置文件
yaml文件更强大的地方在于,它可以给我们的实体类直接注入匹配值!
原来给bean注入属性值的方式:
-
属性名上方@Value
-
加载指定的配置文件
@PropertySource(value = "classpath:tengfei.properties")
在再属性名上方使用Spring的EL表达式@Value("${name}")
现在使用.yaml文件+相应注解的方式来进行属性值的注入
person.yaml配置文件
# SpringBoot的这个配置文件,到底是用来配置什么的?
# 对象
student01:
name: mtf
age: 18
# 行内写法
student02: {name:mtf,age: 18}
# 数组
pets01:
- dog
- cat
pets02: [dog,cat]
person:
name: "Ma \n tengfei"
age: 18
happy: true
birth: 2000/06/14
map: {k1: v1,k2: v2}
list:
- code
- music
- movie
dog:
name: 旺财
age: 3
Person实体类
@ConfigurationProperties(prefix = "person")
public class Person {
private String name;
private Integer age;
private Boolean happy;
private Date birth;
private Map<String,Object> map;
private List list;
private Dog dog;
...
}
执行测试
@SpringBootTest
class SpringBoot02ConfigApplicationTests {
@Autowired
private Person person;
@Test
void contextLoads() {
System.out.println(person.toString());
}
}
注意:需要添加依赖,来防止使用@ConfigurationProperties(prefix = "person")
注解“爆红”
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
4.4、配置文件(.yaml)占位符
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
注意:如果使用.properties文字作为配置文件,需要修改.properties文件默认的编码格式为UTF-8
4.5、方式之间对比
- @ConfigurationProperties只需要写一次即可 , @Value则需要每个字段都添加
- 松散绑定: 比如我的yml中写的last-name,这个和lastName是一样的, - 后面跟着的字母默认是大写的
- JSR303数据校验 , 这个就是我们可以在字段是增加一层过滤器验证 , 可以保证数据的合法性
- 复杂类型封装,yml中可以封装对象 , 使用value就不支持
总结:
- 如果我们在某个业务中,只需要获取配置文件中的某个值,可以使用一下 @value;
- 如果说,我们专门编写了一个JavaBean来和配置文件进行一一映射,就直接@configurationProperties!
4.6、JSR303数据校验
如何使用
Springboot中可以用@validated来校验数据,如果数据异常则会统一抛出异常,方便异常中心统一处理。我们这里来写个注解让我们的name只能支持Email格式;
@Component //注册bean
@ConfigurationProperties(prefix = "person")
@Validated //数据校验
public class Person {
@Email(message="邮箱格式错误") //name必须是邮箱格式
private String name;
}
//报错:Reason: 不是一个合法的电子邮件地址
使用数据校验,可以保证数据的正确性
常见参数
@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 对象是否符合正则表达式的规则
.......等等
除此以外,我们还可以自定义一些数据校验规则
4.7、多环境切换
profile是Spring对不同环境提供不同配置功能的支持,可以通过激活不同的环境版本,实现快速切换环境;
1、配置文件建立位置
2、.properties配置文件的切换
3、.yaml配置文件的切换
-
方式一:
可以在SpringBoot的默认配置文件中加载其他环境的配置文件
server: port: 8081 # 可以选择需要激活哪个配置文件 spring: profiles: active: test
-
方式二:
可以在SpringBoot的默认的核心配置文件中配置多个环境
server: port: 8080 spring: profiles: active: web --- server: port: 8081 spring: config: activate: on-profile: test --- server: port: 8082 spring: config: activate: on-profile: web
五、自动配置再理解
5.1、配置文件该怎么写
在SpringBoot的配置文件中,给特定功能的属性赋值来实现自定义的功能;但是这些属性名(就是在配置过程中给出的属性名的提示)其实是来自XxProperties类中进行定义的,因此可以参照XxProperties相关类来了解相应属性名的含义
5.2、自动配置原理
通过上传框到的,逐步进入底层源码,可以看到最后SpringFactoriesLoader
类里面定义了一个静态常量public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
值得注意的是META-INF/spring.factories这个文件最后被加载并转换成了Properties类型
我们通过文件名全局搜索找到了spring.factories文件:
然后随便点进一个熟悉的:HttpEncodingAutoConfiguration配置类
//部分代码
//出现这个@Configuration和@Bean标志着这个类是个配置类(JavaConfig)--学习Spring中,Java配置类代替Spring的配置文件
@Configuration(
proxyBeanMethods = false
)
//启动指定类的ConfigurationProperties功能;
//进入这个ServerProperties查看,将配置文件中对应的值和ServerProperties绑定起来;
//并把ServerProperties加入到ioc容器中
@EnableConfigurationProperties({ServerProperties.class})
//Spring底层@Conditional注解
//根据不同的条件判断,如果满足指定的条件,整个配置类里面的配置就会生效;
//这里的意思就是判断当前应用是否是web应用,如果是,当前配置类生效
@ConditionalOnWebApplication(
type = Type.SERVLET
)
@ConditionalOnClass({CharacterEncodingFilter.class})
@ConditionalOnProperty(
prefix = "server.servlet.encoding",
value = {"enabled"},
matchIfMissing = true
)
public class HttpEncodingAutoConfiguration {
private final Encoding properties;
public HttpEncodingAutoConfiguration(ServerProperties properties) {
this.properties = properties.getServlet().getEncoding();
}
@Bean
判断容器没有这个组件?
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.web.servlet.server.Encoding.Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.web.servlet.server.Encoding.Type.RESPONSE));
return filter;
}
总结:
- 一但这个配置类生效;这个配置类就会给容器中添加各种组件
- 这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的
- 所有在配置文件中能配置的属性都是在xxxxProperties类中封装着
- 配置文件能配置什么就可以参照某个功能对应的这个属性类
在进入ServerProperties这个类:
这就是自动装配的大概流程!
狂神总结精华:
- SpringBoot启动会加载大量的自动配置类
- 我们看我们需要的功能有没有在SpringBoot默认写好的自动配置类当中
- 我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件存在在其中,我们就不需要再手动配置了)
- 给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们只需要在配置文件中指定这些属性的值即可
- **xxxxAutoConfigurartion:自动配置类;**给容器中添加组件
- xxxxProperties:封装配置文件中相关属性
更多推荐
所有评论(0)