--- title: SpringBoot author: TianZD top: true cover: true toc: true mathjax: false summary: SpringBoot框架学习笔记,粗略学了一下,没有参考价值 tags: - SpringBoot - 学习笔记 categories: - java reprintPolicy: cc_by abbrlink: 6f2612a2 date: 2022-04-29 11:07:58 coverImg: img: password: --- [toc] # SpringBoot # 1、概述 ## 1.1、学习总结 * JavaSE:oop * mysql:持久化 * html+css+js+jquery+框架:视图 * javaWeb:独立开发MVC三层架构的网站 * ssm:框架:简化了开发的流程,配置繁琐 war包:tomcat运行 * spring简化:SpringBoot,jar包,内嵌tomcat,微服务架构 * springcloud:管理微服务 ## 1.2、spring **Spring理念、目的** spring是**为了解决企业级应用开发的复杂性**创建的,**简化开发** **Spring如何简化开发** 1. 基于pojo的轻量级和最小入侵编程 2. 通过IOC,依赖注入(DI)和面向接口实现松耦合 3. 基于切面(AOP)和惯例进行声明式编程 4. 通过切面和模板(template)减少样式代码 > 区别 程序=数据结构+算法:程序员 程序=面向对象+框架:码农 ## 1.3、微服务 > 什么是微服务 微服务是一种**架构风格**,要求在开发一个应用的时候,应用必须构建成一系列小服务的组合,可以通过http的方式通信 > 单体运行架构 all in one,所用的应用服务都封装在一个应用中 > 微服务架构 打破all in one的架构方式,把每个功能元素苏里出来,把苏里出来的功能元素动态组合,需要的功能元素才去拿来组合,需要多一时可以整合多个功能元素,所以微服务架构是对功能元素进行复制,没有对整个应用进行复制 * 节省了调用资源 * 每个功能元素的服务都是一个可替换的,可独立升级的软件代码 ## 1.4、SpringBoot > 概述 Spring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化新 Spring 应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。用我的话来理解,就是 Spring Boot 其实不是什么新的框架,它默认配置了很多框架的使用方式,就像 Maven 整合了所有的 Jar 包,Spring Boot 整合了所有的框架。 > 优势 其实就是简单、快速、方便!平时如果我们需要搭建一个 Spring Web 项目的时候需要怎么做呢? * 1)配置 web.xml,加载 Spring 和 Spring mvc * 2)配置数据库连接、配置 Spring 事务 * 3)配置加载配置文件的读取,开启注解 * 4)配置日志文件 * … * 配置完成之后部署 Tomcat 调试 * … 现在非常流行微服务,如果我这个项目仅仅只是需要发送一个邮件,如果我的项目仅仅是生产一个积分;我都需要这样折腾一遍! 但是如果使用 Spring Boot 呢? 很简单,我仅仅只需要非常少的几个配置就可以迅速方便的搭建起来一套 Web 项目或者是构建一个微服务! ## 1.5、Spring Boot、Spring MVC 和 Spring 分别描述各自的特征: * Spring 框架就像一个**家族**,有众多衍生产品例如 boot、security、jpa等等;但他们的基础都是Spring 的**ioc和 aop**,**ioc 提供了依赖注入的容器**, **aop解决了面向切面编程**,然后在此两者的基础上实现了其他延伸产品的高级功能。 * Spring MVC提供了一种**轻度耦合的方式来开发web应用**;它是**Spring的一个模块**,是**一个web框架**;通过**DispatcherServlet,** ModelAndView 和 View Resolver,开发web应用变得很容易;解决的问题领域是网站应用程序或者服务开发——URL路由、Session、模板引擎、静态Web资源等等。 * Spring Boot实现了auto-configuration**自动配置**(另外三大神器actuator监控,cli命令行接口,starter依赖),降低了项目搭建的复杂度。它主要是为了解决使用Spring框架需要进行大量的配置太麻烦的问题,所以它并不是用来替代Spring的解决方案,而是和Spring框架紧密结合用于提升Spring开发者体验的工具;同时它集成了大量常用的第三方库配置(例如Jackson, JDBC, Mongo, Redis, Mail等等),Spring Boot应用中这些第三方库几乎可以零配置的开箱即用(out-of-the-box)。 所以,用最简练的语言概括就是: * Spring 是一个“引擎”; * Spring MVC 是基于Spring的一个 MVC 框架; * Spring Boot 是基于Spring4的条件注册的一套快速开发整合包。 # 2、SpringBoot程序 ## 2.1、初始化 > 创建: * IDEA新建项目,选择SpringInitializr ![image-20210822110418427](https://i.loli.net/2021/08/22/8Tph41FdNJseuf9.png) * 勾选SpringWeb ![image-20210822110442198](https://i.loli.net/2021/08/22/5FcsUVz69rJuNRa.png) * 新建之后,删除多余文件 ![image-20210822112126048](https://i.loli.net/2021/08/22/Xt3I1BbJvcAu9MQ.png) > 分析 默认自动生成一下文件 * springboot启动文件,启动器 ```java package com.tian.springboot01helloworld; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Springboot01HelloworldApplication { public static void main(String[] args) { SpringApplication.run(Springboot01HelloworldApplication.class, args); } } ``` * application.properties,默认为空 * pop.xml 依赖 ```xml 4.0.0 org.springframework.boot spring-boot-starter-web 2.5.4 spring-boot-starter-web Starter for building web, including RESTful, applications using Spring MVC. Uses Tomcat as the default embedded container https://spring.io/projects/spring-boot Pivotal Software, Inc. https://spring.io Apache License, Version 2.0 https://www.apache.org/licenses/LICENSE-2.0 Pivotal info@pivotal.io Pivotal Software, Inc. https://www.spring.io scm:git:git://github.com/spring-projects/spring-boot.git scm:git:ssh://git@github.com/spring-projects/spring-boot.git https://github.com/spring-projects/spring-boot GitHub https://github.com/spring-projects/spring-boot/issues org.springframework.boot spring-boot-starter 2.5.4 compile org.springframework.boot spring-boot-starter-json 2.5.4 compile org.springframework.boot spring-boot-starter-tomcat 2.5.4 compile org.springframework spring-web 5.3.9 compile org.springframework spring-webmvc 5.3.9 compile ``` * 测试文件 ```java package com.tian.springboot01helloworld; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class Springboot01HelloworldApplicationTests { @Test void contextLoads() { } } ``` > 打jar包 * 在右侧maven中,双击package,自动打jar包 ![image-20210822113501487](https://i.loli.net/2021/08/22/CFH5hvoyu1OsqE3.png) * 在target目录下,生成一个jar包 ![image-20210822113642680](https://i.loli.net/2021/08/22/LaDJh6kZs1glytP.png) * 在命令行进入jar包所在的目录 * 执行`java -jar .\springboot-01-helloworld-0.0.1-SNAPSHOT.jar`![image-20210822114010844](https://i.loli.net/2021/08/22/WiYaEIflxM93GtZ.png) > 新建目录结构 在Springboot启动程序的同级目录下新建controller、dao、pojo、service ![image-20210822112537801](https://i.loli.net/2021/08/22/P3STa8FxzvYUDp1.png) > 新建简单接口 ```java package com.tian.springboot01helloworld.controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @RequestMapping("/hello") public String hello() { // 调用业务,接收前端参数 return "hello,world!"; } } ``` `@RestController` 的意思就是 Controller 里面的方法都以 json 格式输出,不用再写什么 jackjson 配置的了! > 浏览器访问 ## 1.2、配置 > 更改项目端口号 application.properties ```properties # 更改端口号 server.port=8081 ``` > 更改banner * 百度搜索springboot banner在线生成,并复制 * 在resources下创建`banner.txt`,并粘贴进去 ![image-20210822115259816](https://i.loli.net/2021/08/22/5C9B4gzlGaODU7p.png) > 开发环境的热启动 热启动在正常开发项目中已经很常见了吧,虽然平时开发web项目过程中,改动项目启重启总是报错;但springBoot对调试支持很好,修改之后可以实时生效,需要添加以下的配置: ```xml org.springframework.boot spring-boot-devtools true org.springframework.boot spring-boot-maven-plugin true ``` 该模块在完整的打包环境下运行的时候会被禁用。如果你使用 `java -jar`启动应用或者用一个特定的 classloader 启动,它会认为这是一个“生产环境”。 # 3、原理 ## 3.1、启动执行流程 ![img](https://i.loli.net/2021/08/22/tBX3JQApne9H8Mb.png) 上图为SpringBoot启动结构图,我们发现启动流程主要分为三个部分: * 第一部分进行SpringApplication的初始化模块,配置一些基本的环境变量、资源、构造器、监听器; * 第二部分实现了应用具体的启动方案,包括启动流程的监听模块、加载配置环境模块、及核心的创建上下文环境模块; * 第三部分是自动化配置模块,该模块作为springboot自动配置核心,在后面的分析中会详细讨论。在下面的启动程序中我们会串联起结构中的主要功能。 ## 3.2、入口 springboot有自己独立的启动类(独立程序) ```java @SpringBootApplication public class Springboot01HelloworldApplication { public static void main(String[] args) { SpringApplication.run(Springboot01HelloworldApplication.class, args); } } ``` 主要包括: * Annotation定义(@SpringBootApplication),是Spring Boot的核心注解,它其实是一个组合注解 * 类定义(SpringApplication.run):启动整个spring-boot程序,该方法所在类需要使用 ## 3.3、自动配置 ![preview](https://i.loli.net/2021/08/22/AW2O3vsFH4CQGuM.png) ### 3.3.1、@SpringBootApplication ```java @Target``(ElementType.TYPE) ``// 注解的适用范围,其中TYPE用于描述类、接口(包括包注解类型)或enum声明 @Retention``(RetentionPolicy.RUNTIME) ``// 注解的生命周期,保留到class文件中(三个生命周期) @Documented` `// 表明这个注解应该被javadoc记录 @Inherited` `// 子类可以继承该注解 @SpringBootConfiguration` `// 继承了Configuration,表示当前是注解类 @EnableAutoConfiguration` `// 开启springboot的注解功能,springboot的四大神器之一,其借助@import的帮助 @ComponentScan``(excludeFilters = { ``// 扫描路径设置 @Filter``(type = FilterType.CUSTOM, classes = TypeExcludeFilter.``class``), @Filter``(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.``class``) }) public` `@interface` `SpringBootApplication { ... }   ``` ![img](https://i.loli.net/2021/08/22/XyWueDcvs4SgZVa.png) 在其中比较重要的有三个注解,分别是: * @SpringBootConfiguration // 继承了Configuration,表示当前是注解类 * @EnableAutoConfiguration // 开启自动配置 * @ComponentScan(excludeFilters = { // 扫描路径设置(具体使用待确认)扫描主类所在的同级包以及下级包里的Bean 接下来对三个注解一一详解,增加对springbootApplication的理解: #### **@SpringBootConfiguration** 在springboot中我们大多用配置类来解决配置问题 @SpringBootConfiguration继承了Configuration,表示当前是注解类 @Configuration实际上就是一个@Component,表示一个受Spring管理的组件 #### **@ComponentScan** * ComponentScan的功能其实就是自动扫描并加载符合条件的组件(比如@Component和@Repository等)或者bean定义,将这些bean定义加载到**IoC**容器中 * 我们可以通过basePackages等属性来**细粒度**的定制@ComponentScan自动扫描的范围,如果不指定,则**默认**Spring框架实现会从声明@ComponentScan所在类的package进行扫描。 #### **@EnableAutoConfiguration**:自动配置核心 此注解顾名思义是可以**自动配置**,所以应该是springboot中**最为重要的注解**。 ```java @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import({AutoConfigurationImportSelector.class}) ``` 其中最重要的两个注解:1、@AutoConfigurationPackage【重点注解】2、@Import(AutoConfigurationImportSelector.class)【重点注解】 > `@AutoConfigurationPackage` 自动配置包。内部是采用了@Import,来给容器导入一个Registrar组件,**程序运行到这里,会去加载启动类所在包下面的所有类** `@Import`:将一个组件注入容器中 1. **`@Import({Registrar.class})`** ```java static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports { Registrar() { } public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0])); } public Set determineImports(AnnotationMetadata metadata) { return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata)); } } ``` 该类有一个主要方法是**registerBeanDefinitions()**,该方法对容器中进行了批量注册 * AnnotationMetadata(注解元数据类):该类存储了注解相关的位置、属性值等信息 * PackageImports(metadata).getPackageNames():该方法用于返回该注解所在位置的包名信息。(由此可以得到,该位置包名本质其实就是主程序所在的位置): 进入到**register()**方法的底层,发现其功能是将该包名位置下的所有组件添加到容器当中去。 由此可以得到,该注解@AutoConfigurationPackage本质上就是通过导入Register类,将@SpringBootApplication核心注解(即主程序入口)所在位置同层级包下的所有组件扫描注册到Spring容器当中去。这也就解释了为什么在建立dao、service等目录结构的时候,应该在启动程序同级目录下新建 ![image-20210822133236798](https://i.loli.net/2021/08/22/c1SkgxVbsMWRQAn.png) > `@Import(AutoConfigurationImportSelector.class)` ![img](https://i.loli.net/2021/08/22/rZwnhQWpfHlFgVR.png) * 通过@Import往容器中导入注册了一个特殊类**AutoConfigurationImportSelector**。该类中有一个主要方法是**selectImports()** * selectImports()方法主要利用**getAutoConfigurationEntry**(annotationMetadata)给容器中批量导入了一些组件 * getAutoConfigurationEntry方法的主要作用是调用List configurations = getCandidateConfigurations(annotationMetadata, attributes)获取了所有的131个初始候选配置类,并将他们通过一系列筛选过滤后返回上层,最后进一步批量注册到容器中发挥作用。 * 利用工厂加载 Map **loadSpringFactories**(@Nullable ClassLoader classLoader),得到所有的组件 * 从**META-INF/spring.factories**位置来加载一个文件。默认扫描我们当前系统里面所有META-INF/spring.factories位置的文件。 我们发现在项目初始导入的每个Jar包下,都可能包含META-INF/spring.factories这个文件,文件中配置声明了一系列系统类的全类名。而其中比较核心的是spring-boot-autoconfigure-2.5.3.jar这个自动配置包,该文件中使用autoconfigure.EnableAutoConfiguration=声明了131个初始配置类 **总结**: 该注解实际上是通过导入了AutoConfigurationImportSelector这个类,初始化来自动扫描加载所有META-INF/spring.factories这个文件下写死的资源(SpringBoot帮我们配置好了),其中比较核心的是spring-boot-autoconfigure-2.5.3.jar这个自动配置包,该包帮我们初始化加载了131个初始配置类,帮助我们进一步完成SpringBoot的启动和配置功能! > 总结 ![在这里插入图片描述](https://i.loli.net/2021/08/22/7QDXaMqZxFvNAmb.png) ### 3.3.2、自动配置流程总结 ![在这里插入图片描述](https://i.loli.net/2021/08/22/Ge8bvq3DUyktcg2.png) ### 3.3.3、修改默认配置 由上可以知道,自动配置会从META-INF/spring.factories加载初始配置类,初始配置类以XXXAutoConfiguration结尾,如org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration, ![image-20210822174506126](https://i.loli.net/2021/08/22/bxo7Se3w1ZQDFa9.png) 点进去是`WebMvcAutoConfiguration.java`配置类 修改其默认配置可以通过`application.yaml`修改`WebMvcAutoConfiguration.java`配置类中对应的参数 如: ```yaml spring: mvc: static-path-pattern: /** ``` ### 3.3.4、总结 **一句话总结 :根据当前不同的条件判断,决定这个配置类是否生效!** * 一但这个配置类生效;这个配置类就会给容器中添加各种组件; * 这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的; * 所有在配置文件中能配置的属性都是在xxxxProperties类中封装着; * 配置文件能配置什么就可以参照某个功能对应的这个属性类 > 精髓 1、SpringBoot启动会加载大量的自动配置类 2、我们看我们需要的功能有没有在SpringBoot默认写好的自动配置类当中; 3、我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件存在在其中,我们就不需要再手动配置了) 4、给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们只需要在配置文件中指定这些属性的值即可; **xxxxAutoConfigurartion:自动配置类;**给容器中添加组件 **xxxxProperties:封装配置文件中相关属性;** > 了解:@Conditional 了解完自动装配的原理后,我们来关注一个细节问题,**自动配置类必须在一定的条件下才能生效;** **@Conditional派生注解(Spring注解版原生的@Conditional作用)** 作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置配里面的所有内容才生效; ![图片](https://mmbiz.qpic.cn/mmbiz_png/uJDAUKrGC7IPEXZtUAUBhnSZvUmrPzbDGcJRvdK3PtqHPAWYBBmpe1XBVjQJeiatU4vasEaxckHlOga1BV9RPaw/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) **那么多的自动配置类,必须在一定的条件下才能生效;也就是说,我们加载了这么多的配置类,但不是所有的都生效了。** 我们怎么知道哪些自动配置类生效? **我们可以通过启用 debug=true属性;来让控制台打印自动配置报告,这样我们就可以很方便的知道哪些自动配置类生效;** ``` #开启springboot的调试类 debug=true ``` **Positive matches:(自动配置类启用的:正匹配)** **Negative matches:(没有启动,没有匹配成功的自动配置类:负匹配)** **Unconditional classes: (没有条件的类)** ## 3.4、依赖管理 > 父工程 * springboot的父工程中,进行了**资源过滤**、**核心依赖**导入 * 在写或者引入依赖的时候,不需要指定版本 ```xml org.springframework.boot spring-boot-starter-parent 2.5.4 ``` 点进去,发现其父项目为:spring-boot-dependencies ```xml org.springframework.boot spring-boot-dependencies 2.5.4 ``` spring-boot-dependencies声明了很多开发中常用的jar包 ![image-20210822150254879](https://i.loli.net/2021/08/22/NSDrvB8fOjHwYGF.png) 所以在你pom.xml文件中引入jar的时候,如果该jar在`spring-boot-dependencies`中定义了版本号,那么你可以不写。如果你想使用其他的版本号,那么也可以在pom.xml中定义version,遵循就近原则。比如你想使用自定义版本号的mysql驱动,只需在pom.xml中进行定义 ```xml 5.1.43 ``` > 启动器 在SpringBoot项目中,我们只需要引入`spring-boot-starter-web`包就可以写接口并且进行访问,因为在这个starter中整合了我们之前写Spring项目时引入的`spring-aop`、`spring-context`、`spring-webmvc`等jar包,包括tomcat,所以SpringBoot项目不需要外部的tomcat,只需要启动application类使用内置的tomcat服务器即可。 ```xml org.springframework.boot spring-boot-starter-web ``` ```xml org.springframework.boot spring-boot-starter 2.5.4 compile org.springframework.boot spring-boot-starter-json 2.5.4 compile org.springframework.boot spring-boot-starter-tomcat 2.5.4 compile org.springframework spring-web 5.3.9 compile org.springframework spring-webmvc 5.3.9 compile ``` 在SpringBoot项目中,根据官方文档,有各种场景的`spring-boot-starter-*`可以使用,只要引入了starter,这个场景所有常规需要的依赖就会自动引入。 所有场景启动器最底层的依赖就是`spring-boot-starter`,该jar包是核心启动包,包含了自动配置的支持,日志以及YAML。Core starter, including auto-configuration support, logging and YAML,这是官方对它的描述。 ```xml org.springframework.boot spring-boot 2.5.4 compile org.springframework.boot spring-boot-autoconfigure 2.5.4 compile org.springframework.boot spring-boot-starter-logging 2.5.4 compile jakarta.annotation jakarta.annotation-api 1.3.5 compile org.springframework spring-core 5.3.9 compile org.yaml snakeyaml 1.28 compile ``` # 4、yaml ## 4.1、配置文件 SpringBoot使用一个全局的配置文件,配置文件的名称是固定的 * application.properties * 语法:`key=value` * application.yml * 语法:`key: 空格 value` 配置文件的作用:修改SpringBoot自动配置的默认值 ## 4.2、yaml 以前的配置文件都是用xml配置 xml配置: ```xml 8081 ``` yaml配置 ```yaml server: port: 8081 ``` > 基本语法规则 **同一级的字段要对齐,冒号后面要带上空格。** * 大小写敏感 * 使用缩进表示层级关系 * **缩进时不允许使用Tab键,只允许使用空格**。 * 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可 * **#** 表示注释,从这个字符一直到行尾,都会被解析器忽略。 YAML 支持的数据结构有三种。 * 对象:键值对的集合,又称为映射(mapping)/ 哈希(hashes) / 字典(dictionary) * 数组:一组按次序排列的值,又称为序列(sequence) / 列表(list) * 纯量(scalars):单个的、不可再分的值 ```yaml # 普通的key-value name: tian # 对象 student: name: chen age: 18 sex: 男 class: 11 student: {name: tian,age: 18} # 数组 # 一维数组 list: - A - B - C list: [A,B,C] ``` ## 4.3、给属性赋值 > 实体类 Dog.class ```java @Component public class Dog { private String name; private Integer age; // get,set,tostring,constuctor } ``` Person.class ```java @Component public class Person { private String name; private Integer age; private boolean happy; private Date birth; private Map map; private List list; private Dog dog; // get,set,tostring,constuctor } ``` > spring赋值 @Value(“value”) @Autowired ```java public class Dog { @Value("wangzai") private String name; @Value("1") private Integer age; } ``` ```java @SpringBootTest class Springboot02AddvalueApplicationTests { @Autowired private Dog dog; @Test void contextLoads() { System.out.println(dog); } } ``` > yaml赋值 application.yaml ```yaml person: name: tian age: 2 happy: false birth: 2020-01-01 map: {k1: v1,k2: v2} list: - code - jf - fas dog: name: wangcai age: 1 ``` person.class ```java @Component @ConfigurationProperties(prefix = "person") public class Person { ... } ``` > Spring Boot Configuration Annotation Processor not configured 问题解决 ![image-20210822163721616](https://i.loli.net/2021/08/22/QIUvbnGBXkZWFzM.png) * 分析 它的意思是“Spring Boot配置注解执行器没有配置”,配置注解执行器的好处是什么。 配置注解执行器配置完成后,当执行类中已经定义了对象和该对象的字段后,在配置文件中对该类赋值时,便会非常方便的弹出提示信息。 * 解决 在pom.xml文件中引入依赖 ```xml org.springframework.boot spring-boot-configuration-processor true ``` > 松散绑定 在yaml中写的是`last-name`,等同于`lastName` > JSR303数据校验@Validated 可以在字段增加一层过滤器验证,保证数据的合法性 * 导入依赖 pom.xml ```xml org.springframework.boot spring-boot-starter-validation ``` * 添加注解 ```java @Validated public class Person { // message为数据不合法时候的提示信息 @Email(message = "邮箱格式错误") private String email; } ``` * 常见参数 ![在这里插入图片描述](https://i.loli.net/2021/08/22/q89vAVmwOb1frNI.png) * 附加 ![在这里插入图片描述](https://i.loli.net/2021/08/22/wgMAXsk58H4mpWB.png) ```java @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(value) 验证 String 对象是否符合正则表达式的规则 ``` # 5、配置文件 配置文件必须以`application`开头,`application`后面可以有其他东西,`application-test` ## 5.1、配置文件位置 配置文件可以放的位置:从上到下,优先级降低 1. `file:./config/` 2. `file:./` 3. `classpath:/config/` 4. `classpath:/` `file:./`为项目根目录,即`pom.xml`所在的同级目录 `classpath:/`为`src/main/resources`下的目录 ## 5.2、多环境配置 一个项目在开发环境dev、测试环境test、生产环境prod、预发布环境uat,不同的环境会有不同的配置,比如数据库的配置就不同 ### 方法1:修改配置文件 ![img](https://i.loli.net/2021/08/22/6GBxLqInEfD8umt.png) 这种方式的好处就是可以把公共的变量配置在application.properties文件中,不同环境需要的变量配置在不同的文件中,比如数据库信息,线程池的大小,redis信息等。 **修改application.yml配置文件,具体内容如下图:** ![img](https://i.loli.net/2021/08/22/m1WZKIfgTzoQAMD.png) 在启动服务时,服务器就会通过application.yml文件去调用application-dev.yml文件 ### 方法2:命令启动服务,命令中带参数方式(此方式可以没有application.yml文件) 第一步:进入到项目目录下,先用maven对项目进行打包,会在target目录下生成项目的jar包 第二步:进入target目录,执行命令:java -jar 生成的jar包 --spring.profiles.active=prod 项目就会调用application-prod.yml配置文件,即以生产环境的配置要求启动服务。同理,若是开发环境,只需将prod改为dev即可。 # 6、SpringBoot Web开发 自动配置会从`META-INF/spring.factories`加载初始配置类,初始配置类以`XXXAutoConfiguration`结尾 springboot中webMVC开发的自动配置为`org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration`,配置类为:`WebMvcAutoConfiguration.java` ## 6.1、静态资源路径 静态资源路径是指系统可以直接访问的路径,且路径下的所有文件均可被用户通过浏览器直接读取。 > 默认配置 `WebMvcAutoConfiguration.java`配置类中的静态资源路径设置: ```java @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { //用户自定义后,会覆盖默认的 if (!this.resourceProperties.isAddMappings()) { logger.debug("Default resource handling disabled"); return; } //默认路径 addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/"); addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> { registration.addResourceLocations(this.resourceProperties.getStaticLocations()); if (this.servletContext != null) { ServletContextResource resource = new ServletContextResource(this.servletContext, SERVLET_LOCATION); registration.addResourceLocations(resource); } }); } ``` Spring Boot 对静态资源映射提供了默认配置 * **自定义**:自定义后,会把下面两个默认的覆盖 * **webjars(不建议使用)**:`localhost:8080/webjars/` * **classpath**:`localhost:8080/` * `classpath:/static` * `classpath:/public` * `classpath:/resources` * `classpath:/META-INF/resources` ![image-20210822181213875](https://i.loli.net/2021/08/22/c76gZ3X1kUfJVS5.png) 优先级:resources>**static**>public **注意:templates不是静态资源路径** 在src/main/resources目录下新建 public、resources、static 三个目录,并分别放入 1.jpg 2.jpg 3.jpg 三张图片。然后通过浏览器分别访问: ``` http://localhost:8080/1.jpg http://localhost:8080/2.jpg http://localhost:8080/3.jpg ``` 地址均可以正常访问,Spring Boot 默认会从 public resources static 三个目录里面查找是否存在相应的资源。 > 自定义资源路径 `properties.yaml` ```yaml spring: mvc: static-path-pattern: /upload/** ``` 通过spring.mvc.static-path-pattern这种方式配置,会使Spring Boot的默认配置失效,也就是说,/public , /resources 等默认配置不能使用。 配置中配置了静态模式为/upload/**,访问时候就只能通过/upload/xx 来访问。 ``` http://localhost:8080/upload/1.jpg ``` ## 6.2、首页定制 首页可以放在public、resources、static目录下,通过`localhost:8080`直接访问 ![image-20210822182234904](https://i.loli.net/2021/08/22/hNDoU21k9ZXFMYO.png) 在templates下的只能通过controller进行访问 ## 6.3、Thymeleaf模板引擎 ### 模板 模板的诞生是为了将显示与数据分离,模板技术多种多样,但其本质是将模板文件和数据通过模板引擎生成最终的HTML代码。 ![ZWPF0M5W_CR_NEUY6H46__W](https://oss-cn-hangzhou.aliyuncs.com/yqfiles/6c258773c5fc6fca7229b978a7a27b8ff1b326f0.png) 模板技术并不是什么神秘技术,干的是拼接字符串的体力活。模板引擎就是利用正则表达式识别模板标识,并利用数据替换其中的标识符。比如: ```js Hello, <%= name%> ``` 数据是`{name: '木的树'}`,那么通过模板引擎解析后,我们希望得到`Hello, 木的树`。模板的前半部分是普通字符串,后半部分是模板标识,我们需要将其中的标识符替换为表达式。模板的渲染过程如下: ![7J8ICGIRY_4PH_0N_6COAXO](https://oss-cn-hangzhou.aliyuncs.com/yqfiles/e437978f5ecc9f4046d63de6cdcce42bdc308ea2.png) ### 模板引擎 > 什么是模板引擎 **模板引擎**(这里特指用于Web开发的模板引擎)是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的html文档。从字面上理解模板引擎,最重要的就是模板二字,这个意思就是做好一个模板后套入对应位置的数据,最终以html的格式展示出来,这就是模板引擎的作用。 如:**Thymeleaf**,FreeMarker,Enjoy,Velocity,**JSP** > JSP 虽然是一款功能比较强大的模板引擎,并被广大开发者熟悉,但它前后端耦合比较高。比如说前端的html页面还要手动修改成jsp页面,大大加重了工作量,而且动态和静态资源也是耦合性太高。 其次是JSP页面的效率没有HTML高,因为JSP是同步加载。而且JSP需要tomcat,但又不支持nginx等,已经跟不上时代的潮流。 综上:目前开发中已经很少用JSP了,只是我们很多时候会在碰到一些以前的框架里有用到JSP技术,但是技多不压身,推荐还是学一下(如果工作不需要,可以不学)。 ### Thymeleaf介绍 [Thymeleaf 官网](https://www.thymeleaf.org/) > 特点 Thymeleaf 是一个跟 Velocity、FreeMarker 类似的模板引擎,它可以完全替代 JSP 。相较与其他的模板引擎,它有如下三个极吸引人的特点: * Thymeleaf 在有网络和无网络的环境下皆可运行,即它可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。这是由于它支持 html 原型,然后在 html 标签里增加额外的属性来达到模板+数据的展示方式。浏览器解释 html 时会忽略未定义的标签属性,所以 thymeleaf 的模板可以静态地运行;当有数据返回到页面时,Thymeleaf 标签会动态地替换掉静态内容,使页面动态显示。 * Thymeleaf 开箱即用的特性。它提供标准和spring标准两种方言,可以直接套用模板实现JSTL、 OGNL表达式效果,避免每天套模板、该jstl、改标签的困扰。同时开发人员也可以扩展和创建自定义的方言。 * Thymeleaf 提供spring标准方言和一个与 SpringMVC 完美集成的可选模块,可以快速的实现表单绑定、属性编辑器、国际化等功能。 > **动静分离**: 浏览器无法直接识别`.jsp`文件,需要借助网络(服务端)才能进行访问;而Thymeleaf用html做模板可以直接在浏览器中打开。开发者充分考虑html页面特性,将Thymeleaf的语法通过html的标签属性来定义完成,这些标签属性不会影响html页面的完整性和显示。如果通过后台服务端访问页面服务端会寻找这些标签将服务端对应的数据替换到相应位置实现动态页面!大体区别可以参照下图: ![在这里插入图片描述](https://i.loli.net/2021/08/22/wQybk5IetuFZx1M.png) 上图的意思就是如果直接打开这个html那么浏览器会对`th`等标签忽视而显示原始的内容。如果通过服务端访问那么服务端将先寻找`th`标签将服务端储存的数据替换到对应位置。具体效果可以参照下图,下图即为一个动静结合的实例。 ### 使用Thymeleaf 对于构建一个完整程序,创建第一个Thymeleaf程序需要以下几个步骤: 1. 创建程序,添加依赖 2. 编写Controller 3. 编写Thymeleaf页面:需要放在`templates`目录下 4. 访问页面 > 创建程序,添加依赖 * 方法1:在创建程序的时候,勾选其中Web 模块的Spring web依赖以及Template 模块的Thymeleaf依赖 ![image-20210822201028792](https://i.loli.net/2021/08/22/loQriWv6zZKFAmR.png) * 方法2:在pom.xml中添加以下依赖 ```xml org.springframework.boot spring-boot-starter-thymeleaf org.springframework.boot spring-boot-starter-web ``` > 编写controller ```java @Controller public class ThymeleafController { @RequestMapping("/test") public String test1(Model model) { model.addAttribute("msg", "hello,thymeleaf"); return "test"; } } ``` > 编写Thymeleaf页面 咱们在项目的resources目录下的templates文件夹下面创建一个叫index.html的文件,咱们在这个html文件中的``标签修改为``这样在Thymeleaf中就可以使用Thymeleaf的语法和规范啦。 `test.html` ```html Title

tian

``` > 浏览器测试 * 直接打开页面 ![image-20210822204040165](https://i.loli.net/2021/08/22/Z7koXvl2EKx4SNt.png) * 通过服务端访问 会进行动态替换 ![image-20210822204025137](https://i.loli.net/2021/08/22/CeE5Xuxaf7mKt1o.png) ### thymeleaf语法 > 标签 Thymeleaf通过特殊的标签来寻找属于Thymeleaf的部分,并渲染该部分内容。Thymeleaf也主要通过标签来识别替换对应位置内容,Thymeleaf标签有很多很多,功能也很丰富,这里列举一些比较常用的标签如下: | 标签 | 作用 | 示例 | | :-------- | :----------------- | :----------------------------------------------------------- | | th:id | 替换id | `` | | th:text | 文本替换 | `

bigsai

` | | th:utext | 支持html的文本替换 | `

content

` | | th:object | 替换对象 | `
` | | th:value | 替换值 | `` | | th:each | 迭代 | `` | | th:href | 替换超链接 | `超链接` | | th:src | 替换资源 | `` | > 链接表达式@{…} 上面我们已经学习到Thymeleaf是一个基于html的模板引擎,但是我们还是需要加入特定标签来声明和使用Thymeleaf的语法。我们需要在Thymeleaf的头部加Thymeleaf标识: ```html ``` 在Thymeleaf 中,如果想引入链接比如link,href,src,需要使用`@{资源地址}`引入资源。其中资源地址可以static目录下的静态资源,也可以是互联网中的绝对资源。 **引入css** ```html ``` **引入JavaScript:** ```html ``` **超链接:** ```html 超链接 ``` > 变量表达式: ${...} 在Thymeleaf中可以通过${…}进行取值,这点和ONGL表达式语法一致。 **取普通字符串:** 如果在controller中的Model直接存储某字符串,我们可以直接`${对象名}`进行取值。完整代码如下: ```html

普通字符串

``` **取JavaBean对象:** 取JavaBean对象也很容易,因为JavaBean自身有一些其他属性,所以咱们就可以使用`${对象名.对象属性}`或者`${对象名['对象属性']}`来取值,这和JavaScript语法是不是很相似呢!除此之外,如果该JavaBean如果写了get方法,咱们也可以通过get方法取值例如`${对象.get方法名}`完整代码如下: ```html

JavaBean对象

介绍
年龄
介绍
``` **取List集合(each):** 因为List集合是个有序列表,里面内容可能不止一个,你需要遍历List对其中对象取值,而遍历需要用到标签:`th:each`,具体使用为` `,其中item就相当于遍历每一次的对象名,在下面的作用域可以直接使用,而userlist就是你在Model中储存的List的名称。完整的代码为: ```html

List取值

``` **直接取Map:** 很多时候我们不存JavaBean而是将一些值放入Map中,再将Map存在Model中,我们就需要对Map取值,对于Map取值你可以`${Map名['key']}`来进行取值。也可以通过`${Map名.key}`取值,当然你也可以使用`${map.get('key')}`(java语法)来取值,完整代码如下: ```html

Map取值

place:
feeling:
``` **遍历Map:** 如果说你想遍历Map获取它的key和value那也是可以的,这里就要使用和List相似的遍历方法,使用`th:each="item:${Map名}"`进行遍历,在下面只需使用`item.key`和`item.value`即可获得值。完整代码如下: ```html

Map遍历

``` > 选择表达式 * if-then:`(if) ? (then)` * if-then-else:`(if) ? (then) : (else)` * default : `(value) ?: (defaultvalue)` > 选择变量表达式: *{...} 变量表达式不仅可以写成${...},而且还可以写成*{...}。 但是,有一个重要的区别:星号语法对选定对象而不是整个上下文评估表达式。也就是说,只要没有选定的对象,美元(`${…}`)和星号(`*{...}`)的语法就完全一样。 什么是选定对象?使用`th:object`属性的表达式的结果。就可以选定对象,具体实例如下: ```html

Name: .

Age: 18.

Detail: 好好学习.

``` 当然`*{…}`也可和`${…}`混用。上面的代码如果不使用选定对象,完全等价于: ```html

Name: .

Age: 18.

Detail: 好好学习.

``` > 消息表达: #{...} 文本外部化是从模板文件中提取模板代码的片段,以便可以将它们保存在单独的文件(通常是.properties文件)中,文本的外部化片段通常称为“消息”。通俗易懂的来说`#{…}`语法就是用来**读取配置文件中数据**的。在Thymeleaf你可以使用`#{...}`语法获取消息,具体实例代码如下: 首先在templates目录下建立`home.properties`中写入以下内容: ```properties bigsai.nane=bigsai bigsai.age=22 province=Jiang Su ``` 在`application.properties`中加入以下内容: ```properties spring.messages.basename=templates/home ``` 这样我们就可以在Thymeleaf中读取配置的文件了,完整代码如下: ```html

消息表达

name
年龄
province
``` > 抽取公共页面 网页中很多具有公共的部分,可以把公共的部分抽取出来,放在resources/commons/commons.html下 * 定义公共部分 ```html

//公共部分

``` * 使用公共部分 ```html
或者用th:replace ``` ## 6.4、扩展SpringMVC > 扩展 Springboot在自动配置很多组件的时候,先看容器中有没有用户自己配置的,如果有就用用户自己配置的,如果没有就用自动配置的,如果有些组件可以存在多个,比如视图解析器,就将用户配置的和自己默认的组合起来 * 编写一个**@Configuration注解类**,类型要为**WebMvcConfigurer**,还**不能标注@EnableWebMve**注解,一般放在config包下、 * 重写相应的方法 ```java @Configuration public class MyMvcConfig implements WebMvcConfigurer { //视图跳转 @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/tiaozhuan").setViewName("test"); } } ``` 如上,重写了addViewControllers方法,当浏览器访问:`http://localhost:8080/tiaozhuan`时,会跳转到templates下的test.html页面 # 7、员工管理系统 ## 7.1、准备工作 > 资源下载 [资源下载](https://www.kuangstudy.com/app/code) 下载完成后放置资源 ![image-20210822221308597](https://i.loli.net/2021/08/22/KzmHa4GXAy3PrMj.png) > 数据 ## 7.2、首页 ```html Signin Template for Bootstrap ``` > 首页访问 首页`index.html`在templates目录下,不能直接访问 通过下面两种方法设置后,可以通过`http://localhost:8080`和`http://localhost:8080/index.html`访问 * 方法1:通过增加controller ```java @Controller public class IndexController { @RequestMapping({"/","/index.html"}) public String index() { return "index"; } } ``` * 方法2:通过扩展springmvc ```java @Configuration public class MyMvcCfonfig implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("index"); registry.addViewController("/index.html").setViewName("index"); } } ``` ![image-20210823012558877](https://i.loli.net/2021/08/23/SIHr3X4TCnJDx9L.png) > 首页Thymeleaf 所有页面的静态资源都需要使用thymeleaf接管:`th:href=“@{}”` 使用thymeleaf语法对首页中的链接进行更改 ```html # 略 # 略 ``` ![image-20210823014357633](https://i.loli.net/2021/08/23/MosWyf5FaOxSzHj.png) ## 7.3、页面国际化 中英文语言切换 > 设置idea 设置为utf-8 ![image-20210823015246261](https://i.loli.net/2021/08/23/gsAdhG4NM157Fjc.png) > 编写国际化配置文件 ![image-20210823021454836](https://i.loli.net/2021/08/23/noXT1emdif7EILB.png) * login.properties ```properties login.btn=登录 login.password=密码 login.remember=记住我 login.tip=请登录 login.username=用户名 ``` * login_en_US.properties ```properties login.btn=sign in login.password=password login.remember=remember me login.tip=please sign in login.username=username ``` * login_zh_CN.properties ```properties login.btn=登录 login.password=密码 login.remember=记住我 login.tip=请登录 login.username=用户名 ``` > 配置文件位置 国际化自动配置类:MessageSourceAutoConfiguration.class 在application.properties中配置 ```properties # 配置g国际化文件位置 spring.messages.basename=i18n.login ``` > 页面中设置国际化消息Thymeleaf Thymeleaf语法:`th:text=“#{}”` ```html

Please sign in

Please sign in

``` ![image-20210823022834729](https://i.loli.net/2021/08/23/fk2Stqug5PNR1mO.png) > 语言切换 国际化语言的切换主要是因为有一个区域信息解析器在其作用。 是根据HttpServletRequest中的locale属性来判定启用哪个语言文件的。 我们的需求是通过点击链接来切换语言,那么我们可以自定义一个区域信息解析器来替代这个默认的解析器。 * 首先,配置类扩展mvc ```java @Bean public LocaleResolver localeResolver(){ return new NativeLocaleResolver(); } protected static class NativeLocaleResolver implements LocaleResolver{ @Override public Locale resolveLocale(HttpServletRequest request) { String language = request.getParameter("language"); Locale locale = Locale.getDefault(); if(!StringUtils.isEmpty(language)){ String[] split = language.split("_"); locale = new Locale(split[0],split[1]); } return locale; } @Override public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) { } } ``` * 编写页面 ```html 中文 English ``` ## 7.4、登录 > index.html ```html