目录
Spring缺点分析
什么是SpringBoot
SpringBoot核心功能
SpringBoot项目结构
POM文件
启动类、配置文件
SpringBoot入门案例
原理分析
起步依赖
自动配置
核心注解
YAML文件
配置文件介绍
自定义配置简单数据
自定义配置对象数据
自定义配置集合数据
@Value读取配置文件数据
@ConfigurationProperties
占位符的作用
SpringBoot访问静态资源
静态资源相关目录
静态资源其他存放位置
重点:SpringBoot项目静态图片加载浏览器不显示解决方案
Thymeleaf
入门
变量输出
操作字符串
操作时间
条件判断
遍历集合
遍历时使用状态变量
遍历map
URL写法
RESTful风格URL写法
相关配置
SpringBoot参数校验
简单数据类型
异常处理
校验相关注解
对象类型
SpringTask
定时任务
入门案例
Cron表达式
@Schedule
多线程任务
部分图片来自百战程序员
Spring是一个非常优秀的轻量级框架,以IOC(控制反转)和AOP(面向切面)为思想内核,极大简化了JAVA企业级项目的开发。
虽然Spring的组件代码是轻量级的,但它的配置却是重量级的。使用Spring进行项目开发需要在配置文件中写很多代码,所有这些配置都代表了开发时的损耗。
除此之外,Spring项目的依赖管理也是一件耗时耗力的事情。在环境搭建时,需要分析要导入哪些库的坐标,而且还需要分析导入与之有依赖关系的其他库的坐标,一旦选错了依赖的版本,随之而来的不兼容问题就会严重阻碍项目的开发进度。比如Spring5.0以上只能使用Junit4.12以上的版本。
总结
Spring的缺点:
SpringBoot对Spring的缺点进行改善和优化,基于约定大于配置的思想,简化了Spring的开发,所谓简化是指简化了Spring中大量的配置文件和繁琐的依赖引入。所以SpringBoot是一个服务于框架的框架,它不是对Spring功能的增强,而是提供了一种快速使用Spring框架的方式。
SpringBoot的优点:
自动配置
SpringBoot项目自动提供最优配置,同时可以修改默认值满足特定的要求。
起步依赖
SpringBoot的依赖是基于功能的,而不是普通项目的依赖是基于JAR包的。SpringBoot将完成一个功能所需要的所有坐标打包到一起,并完成了版本适配,我们在使用某功能时只需要引入一个依赖即可。
接下来我们分析SpringBoot的项目结构:
POM文件
org.springframework.boot
spring-boot-starter-parent
2.7.0-M1
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-maven-plugin
启动类
启动类的作用是启动SpringBoot项目,运行启动类的main方法即可启动SpringBoot项目。
@SpringBootApplication
public class Springbootdemo2Application{
public static void main(String[] args) {
SpringApplication.run(Springbootdemo2Application.class, args);
}
}
配置文件
由于SpringBoot极大简化了Spring配置,所以只有一个application.properties配置文件,且Spring的自动配置功能使得大部分的配置都有默认配置,该文件的功能是覆盖默认配置信息,该文件不写任何信息都可以启动项目。
启动后默认端口号为8080,我们可以覆盖该配置:
server.port=8888
1、通过IDEA搭建SpringBoot项目,点击新建项目,选中Spring Initializr,点击下一步
2、编写包结构,项目名称,项目类型等等
3、勾选起步依赖,然后创建项目
4、在启动类同级包下或同级子包下创建控制器
@Controller
public class MyController {
@RequestMapping("/hello")
@ResponseBody
public String hello(){
System.out.println("hello");
return "myController";
}
}
5、运行启动类,访问对应路径
查看spring-boot-starter-parent起步依赖
查看spring-boot-starter-web起步依赖
按住Ctrl点击pom.xml中的spring-boot-starter-web,跳转到了spring-boot-starter-web的pom.xml,从spring-boot-starter-web的pom.xml中我们可以发现,spring-boot-starter-web就是将web开发要使用的spring-web、spring-webmvc等坐标进行了打包,这样我们的工程只要引入spring-boot-starter-web起步依赖的坐标就可以进行web开发了,同样体现了依赖传递的作用。
@SpringBootConfiguration:等同与@Configuration,既标注该类是Spring的一个配置类
@EnableAutoConfiguration:SpringBoot自动配置功能开启
@Import(AutoConfigurationImportSelector.class) 导入了AutoConfigurationImportSelector类
SpringFactoriesLoader.loadFactoryNames方法的作用就是从META-INF/spring.factories文件中读取指定类对应的类名称列表
有关配置类的信息如下:
上面配置文件存在大量的以Configuration为结尾的类名称,这些类就是存有自动配置信息的类,而SpringApplication在获取这些类名后再加载。
@EnableConfifigurationProperties(ServerProperties.class)代表加载ServerProperties服务器配置属性类。
prefifix = "server"表示SpringBoot配置文件中的前缀,SpringBoot会将配置文件中以server开始的属性映射到该类的字段中。所以配置网络端口的方式为server.port
该文件中保存的就是所有默认配置信息。
@SpringBootApplication
标注是SpringBoot的启动类。
此注解等同于@SpringBootConfiguration+@EnableAutoConfiguration+@ComponentScan。
@SpringBootConfiguration
@SpringBootConfiguration是@Configuration的派生注解,跟@Configuration功能一致,标注这个类是一个配置类,只不过@SpringBootConfiguration是Springboot的注解,而@Configuration是Spring的注解
@EnableAutoConfiguration
SpringBoot自动配置注解。
等同于@AutoConfigurationPackage+@Import(AutoConfigurationImportSelector.class)
@AutoConfigurationPackage
自动扫描包的注解,它会自动扫描主类所在包下所有加了注解的类(@Controller,@Service等),以及配置类(@Configuration)。
@Import({AutoConfigurationImportSelector.class})
该注解会导入AutoConfifigurationImportSelector类对象,该对象会从META-INF/spring.factories文件中读取配置类的名称列表。
@ComponentScan
该注解会扫描项目,自动装配一些项目启动需要的Bean。
SpringBoot项目中,大部分配置都有默认值,但如果想替换默认配置的话,就可以使用application.properties或者application.yml进行配置。
SpringBoot默认会从resources目录下加载application.properties或application.yml文件。其中,application.properties文件是键值对类型的文件,之前一直在使用,所以我们不再对properties文件进行阐述。
https://docs.spring.io/spring-boot/docs/2.7.0-M1/reference/htmlsingle/#application-properties.server可以查找配置文件如何覆盖SpringBoot项目的默认配置
除了properties文件外,SpringBoot还支持YAML文件进行配置。YAML文件的扩展名为.yml或.yaml,它的基本要求如下:
比如使用properties文件配置tomcat端口:
server.port=8888
而使用YAML文件配置tomcat端口:
server:
port: 8888
除了覆盖默认配置,我们还可以在YAML文件中配置其他信息以便我们在项目中使用。配置简单数据的方式如下:
注意:冒号后面必须有一个空格
属性名前面的空格个数不限,在yml语法中,相同缩进代表同一个级别,只要每个属性前的空格数一样即可。
注意:值与之前的 - 之间存在一个空格
我们可以通过@Value注解将配置文件中的值映射到一个Spring管理的Bean的字段上,用法如下:
读取上次编写的文件数据
application.yml:
name: zhangsan
student1:
name: zhangsan
age: 14
student2: {name: zhangsan,age: 15}
city1:
- beijign
- shanghai
- tianjing
city2: [beijing,shanghai,tianjing]
students:
- name: zhangsan
age: 16
sex: male
- name: lisi
age: 20
sex: female
- name: wangwu
age: 25
sex: male
编写控制器:
@Controller
public class YmlController {
@Value("${name}")
private String name;
@Value("${student1.age}")
private int age;
@Value("${city1[0]}")
private String city1;
@Value("${students[0].sex}")
private String sex;
@RequestMapping("/yml")
@ResponseBody
public String ymlController(){
return name+":"+age+":"+city1+":"+sex;
}
}
运行启动类,访问对应路径
@ConfigurationProperties(prefix="key")
读取的类需要编写getter和setter方法,否则无法赋值
application.yml:
user:
id: 10001
username: shangxuetang
address:
- beijing
- tianjin
- shanghai
- chongqing
grades:
- subject: math
score: 100
- subject: english
score: 90
编写控制器
package com.itbaizhan.springboot_blog.contoller;
import com.itbaizhan.springboot_blog.domain.Grade;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
@Controller
@ConfigurationProperties(prefix = "user")
public class YmlController1 {
private int id;
private String username;
private List address;
private List grades;
@RequestMapping("/yml2")
@ResponseBody
public String yml2(){
System.out.println(id);
System.out.println(username);
System.out.println(address);
System.out.println(grades);
return "hello springboot!";
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public List getAddress() {
return address;
}
public void setAddress(List address) {
this.address = address;
}
public List getGrades() {
return grades;
}
public void setGrades(List grades) {
this.grades = grades;
}
}
YAML文件中可以使用${}占位符,它有两个作用:
1、使用配置文件中的值
@Controller
public class YmlController3{
@Value("${myconfig.myport}")
private int port;
@RequestMapping("/yml3")
@ResponseBody
public String yml3(){
System.out.println(port);
return "hello springboot!";
}
}
2、SpringBoot框架提供了一些生成随机数的方法可以在yml文件中使用:
用法如下:
# 随机生成tomcat端口
server:
port: ${random.int(1024,9999)}
SpringBoot项目中没有WebApp目录,只有src目录。在src/main/resources下面有static和templates两个文件夹。SpringBoot默认在static目录中存放静态资源,而templates中放动态页面。
static目录
SpringBoot通过/resources/static目录访问静态资源,在resources/static中编写html页面:
测试html
我的HTML
目录结构
templates目录
在SpringBoot中不推荐使用JSP作为动态页面,而是默认使用Thymeleaf编写动态页面。templates目录是存放Thymeleaf页面的目录,稍后我们讲解Thymeleaf技术。
除了/resources/static目录,SpringBoot还会扫描以下位置的静态资源:
我们还可以在配置文件自定义静态资源位置
在SpringBoot配置文件进行自定义静态资源位置配置
spring:
web:
resources:
static-locations: classpath:/suibian/,classpath:/static/
注意:
SpringBoot项目静态图片加载浏览器不显示问题解决方案
项目结构如下:
我是通过Maven创建的以Thymeleaf为模板引擎创建的SpringBoot Web项目,发现加载的图片在浏览器不显示,本来我以为是配路径加载错误,后来发现路径并没有问题,我的图片放在src/main/resources/static/images目录中,在前端加载代码如下:${aBook.picture}是获取模型的图片名称。
这样看起来其实没有任何问题,但是就是浏览器不显示图片,后面我以为需要像springMVC一样,需要配置静态资源路径,进行映射,防止拦截器拦截,我就在全局配置文件application.properties下配置了路径,后面发现根本没有必要配置,配置了也没有意义,因为SpringBoot默认访问static目录,所以最终的解决方法就是更新缓存并重启项目,就可以正常显示了。
如下所示:
Thymeleaf是一款用于渲染XML/HTML5内容的模板引擎,类似JSP。它可以轻易的与SpringMVC等Web框架进行集成作为Web应用的模板引擎。在SpringBoot中推荐使用Thymeleaf编写动态页面。
Thymeleaf最大的特点是能够直接在浏览器中打开并正确显示模板页面,而不需要启动整个Web应用。
Thymeleaf在有网络和无网络的环境下皆可运行,它即可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。没有数据时,Thymeleaf的模板可以静态地运行;当有数据返回到页面时,Thymeleaf标签会动态地替换掉静态内容,使页面动态显示。
org.springframework.boot
spring-boot-starter-thymeleaf
org.springframework.boot
spring-boot-starter-web
创建视图index.html
thymeleaf入门
hello
template中的html文件不能直接访问,需要编写Controller跳转到页面中
@Controller
public class PageController {
// 页面跳转
@GetMapping("/show")
public String showPage(Model model){
model.addAttribute("msg","Hello Thymeleaf");
return "index";
}
}
启动项目,访问 http://localhost:8080/show
再访问静态页面
语法 |
作用 |
th:text |
将model中的值作为内容放入标签中 |
th:value |
将model中的值放入input标签的value属性中 |
@GetMapping("/show")
public String showPage(Model model){
model.addAttribute("msg","Hello Thymeleaf");
return"index";
}
thymeleaf入门
hello
Thymeleaf提供了一些内置对象可以操作数据,内置对象可直接在模板中使用,这些对象是以#引用的,
操作字符串的内置对象为strings。
方法 |
说明 |
${#strings.isEmpty(key)} |
判断字符串是否为空,如果为空返回true,否则返回false |
${#strings.contains(msg,'T')} |
判断字符串是否包含指定的子串,如果包含返回true,否则返回false |
${#strings.startsWith(msg,'a')} |
判断当前字符串是否以子串开头,如果是返回true,否则返回false |
${#strings.endsWith(msg,'a')} |
判断当前字符串是否以子串结尾,如果是返回true,否则返回false |
${#strings.length(msg)} |
返回字符串的长度 |
${#strings.indexOf(msg,'h')} |
查找子串的位置,并返回该子串的下标,如果没找到则返回-1 |
${#strings.substring(msg,2,5)} |
截取子串,用法与JDK的subString方法相同 |
${#strings.toUpperCase(msg)} |
字符串转大写 |
${#strings.toLowerCase(msg)} |
字符串转小写 |
使用方式:
thymeleaf入门
hello
操作时间的内置对象为dates
方法 |
说明 |
${#dates.format(key)} |
格式化日期,默认的以浏览器默认语言为格式化标准 |
${#dates.format(key,'yyyy/MM/dd')} |
按照自定义的格式做日期转换 |
${#dates.year(key)} |
取年 |
${#dates.month(key)} |
取月 |
${#dates.day(key)} |
取日 |
语法 |
作用 |
th:if |
条件判断 |
model.addAttribute("sex","女");
性别:男
性别:女
语法 |
作用 |
th:switch/th:case |
th:switch/th:case与Java中的switch语句等效。th:case="*"表示Java中switch的default,即没有case的值为true时显示th:case="*"的内容。 |
语法 |
作用 |
th:each |
迭代器,用于循环迭代集合 |
遍历集合
编写实体类
public class Users{
private String id;
private String name;
private int age;
// 省略getter/setter/构造方法
}
准备数据
List users=new ArrayList<>();
users.add(new Users("1","sxt",23));
users.add(new Users("2","baizhan",22));
users.add(new Users("3","admin",25));
model.addAttribute("users",users);
在页面中展示数据
ID
Name
Age
thymeleaf将遍历的状态变量封装到一个对象中,通过该对象的属性可以获取状态变量:
状态变量 |
含义 |
index |
当前迭代器的索引,从0开始 |
count |
当前迭代对象的计数,从1开始 |
size |
被迭代对象的长度 |
odd/even |
布尔值,当前循环是否是偶数/奇数,从0开始 |
first |
布尔值,当前循环的是否是第一条,如果是返回true,否则返回false |
last |
布尔值,当前循环的是否是最后一条,如果是则返回true,否则返回false |
使用状态变量
遍历出的是一个键值对对象,key获取键,value获取值
准备数据
Map map=new HashMap<>();
map.put("user1",new Users("1","shangxuetang",23));
map.put("user2",new Users("2","baizhan",22));
map.put("user3",new Users("3","admin",25));
model.addAttribute("map",map);
遍历map
ID
Name
Age
Key
获取域中的数据
thymeleaf也可以获取request,session,application域中的数据,方法如下:
准备数据
request.setAttribute("req","HttpServletRequest");
session.setAttribute("ses","HttpSession");
session.getServletContext().setAttribute("app","application");
获取域数据
request1:
request2:
session1:
session2:
application1:
application2:
在Thymeleaf中路径的写法为@{路径}
th:href="@{http://www.baidu.com}">百度
在路径中添加参数
model.addAttribute("id","100");
model.addAttribute("name","bzcxy");
@GetMapping("/show2")
@ResponseBody
public String show2(String id,String name){
return id+":"+name;
}
静态参数一
静态参数二
动态参数一
动态参数二
@GetMapping("/show3/{id}/{name}")
@ResponseBody
public String show3(@PathVariable String id,@PathVariable String name){
return id+":"+name;
}
restful格式传递参数方式
在Springboot配置文件中可以进行Thymeleaf相关配置
配置项 |
含义 |
spring.thymeleaf.prefix |
视图前缀 |
spring.thymeleaf.suffix |
视图后缀 |
spring.thymeleaf.encoding |
编码格式 |
spring.thymeleaf.servlet.content-type |
响应类型 |
spring.thymeleaf.cache=false |
页面缓存,配置为false则不启用页面缓存,方便测试 |
spring:
thymeleaf:
prefix: classpath:/templates/
suffix: .html
encoding: UTF-8
cache: false
servlet:
content-type: text/html
SpringBoot自带了validation工具可以从后端对前端传来的参数进行校验,
用法如下:
在类上方添加@Validated注解
在参数前添加@NotBlank注解
引入validation起步依赖
org.springframework.boot
spring-boot-starter-validation
编写Controller
// 该控制器开启参数校验
@Validated
@Controller
public class TestController{
@RequestMapping("/t1")
@ResponseBody
// 在参数前加校验注解,该注解的意思是字符串参数不能为null
public String t1(@NotBlank String username){
System.out.println(username);
return "请求成功!";
}
}
访问 http://localhost:8080/t1
如果没有传递参数则会报异常
在校验参数的注解中添加message属性,可以替换异常信息。
// 该控制器开启参数校验
@Validated
@Controller
public class TestController{
@RequestMapping("/t1")
@ResponseBody
// 在参数前加校验注解,该注解的意思是字符串参数不能为null
public String t1(@NotBlank(message="用户名不能为空") String username){
System.out.println(username);
return "请求成功!";
}
}
当抛出ConstraintViolationException异常后,我们可以使用SpringMVC的异常处理器,也可以使用SpringBoot自带的异常处理机制。
当程序出现了异常,SpringBoot会使用自带的BasicErrorController对象处理异常。该处理器会默认跳转到/resources/templates/error.html页面。
编写异常页面:
错误页面
服务器开小差了!
我们再次访问t2并且不传递参数
SpringBoot参数校验_校验相关注解
注解 |
作用 |
@NotNull |
判断包装类是否为null |
@NotBlank |
判断字符串是否为null或者是空串(去掉首尾空格) |
@NotEmpty |
判断集合是否为空 |
@Length |
判断字符的长度(最大或者最小) |
@Min |
判断数值最小值 |
@Max |
判断数值最大值 |
|
判断邮箱是否合法 |
@RequestMapping("/t2")
@ResponseBody
public String t2(
@NotBlank @Length(min=1, max=5) String username,
@NotNull @Min(0) @Max(150) Integer age,
@NotEmpty @RequestParamList address,
@NotBlank @Email String email) {
System.out.println(username);
System.out.println(age);
System.out.println(address);
System.out.println(email);
return "请求成功!";
}
校验的对象参数前添加@Validated,并将异常信息封装到BindingResult对象中
SpringBoot也可以校验对象参数中的每个属性,用法如下:
添加实体类
public class Student{
@NotNull(message="id不能为空")
private Integer id;
@NotBlank(message="姓名不能为空")
private String name;
// 省略getter/setter/tostring
}
编写控制器
@Controller
public class TestController2{
@RequestMapping("/t3")
@ResponseBody
// 校验的对象参数前添加@Validated,并将异常信息封装到BindingResult对象中
public String t3(@Validated Student student,BindingResult result) {
// 判断是否有参数异常
if(result.hasErrors()) {
// 所有参数异常
List list = result.getAllErrors();
// 遍历参数异常,输出异常信息
for(ObjectError err : list) {
FieldError fieldError = (FieldError) err;
System.out.println(fieldError.getDefaultMessage());
}
return "参数异常";
}
System.out.println(student);
return "请求成功!";
}
}
定时任务即系统在特定时间执行一段代码,它的场景应用非常广泛:
定时任务的实现主要有以下几种方式:
1、创建SpringBoot项目,
在启动类上方添加@EnableScheduling注解开启定时任务。
2、编写定时任务类
方法上方添加 @Scheduled ,将该方法设置为定时方法,并且需要将定时类放到Spring容器中
@Component
public class Task1 {
@Scheduled(cron = "* * * * * *")
public void t1(){
SimpleDateFormat sdf = new SimpleDateFormat("HH-mm-ss");
System.out.println(sdf.format(new Date()));
}
}
3、启动项目,定时任务方法按照配置定时执行。
Spring Task依靠Cron表达式配置定时规则。Cron表达式是一个字符串,分为6或7个域,每一个域代表一个含义,以空格隔开。有如下两种语法格式:
Seconds(秒):域中可出现, - * /四个字符,以及0-59的整数
Minutes(分),Hours(时)同上
DayofMonth(日期):域中可出现, - * / ? L W C八个字符,以及1-31的整数
Month(月份):域中可出现, - * /四个字符,以及1-12的整数或JAN-DEC的单词缩写
DayofWeek(星期):可出现, - * / ? L # C八个字符,以及1-7的整数或SUN-SAT 单词缩写,1代表星期天,7代表星期六
Year(年份):域中可出现, - * /四个字符,以及1970~2099的整数。该域可以省略,表示每年都触发。
@Scheduled写在方法上方,指定该方法定时执行。常用参数如下:
Spring Task定时器默认是单线程的,如果项目中使用多个定时器,使用一个线程会造成效率低下。代码如下:
@Component
public class Task1 {
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
@Scheduled(cron = "* * * * * *")
public void t1(){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getId()+":线程1,时间:"+sdf.format(new Date()));
}
@Scheduled(cron = "* * * * * *")
public void t2(){
System.out.println(Thread.currentThread().getId()+":线程2,时间:"+sdf.format(new Date()));
}
}
任务1较浪费时间,会阻塞任务2的运行。此时我们可以给Spring Task配置线程池。
通过配置类实现SchedulingConfigurer接口,重写方法
@Configuration
public class SchedulingConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
//创建线程池
taskRegistrar.setScheduler(Executors.newScheduledThreadPool(5));
}
}
此时任务1不会阻塞任务2的运行