• SpringBoot入门


    目录

    Spring缺点分析

    什么是SpringBoot

    SpringBoot核心功能

    SpringBoot项目结构

            POM文件

            启动类、配置文件

    SpringBoot入门案例

    原理分析

            起步依赖

            自动配置

            核心注解

    YAML文件

            配置文件介绍

            自定义配置简单数据

            自定义配置对象数据

            自定义配置集合数据

            @Value读取配置文件数据

            @ConfigurationProperties

            占位符的作用

    SpringBoot访问静态资源

            静态资源相关目录

            静态资源其他存放位置

            重点:SpringBoot项目静态图片加载浏览器不显示解决方案

    Thymeleaf

            入门

            变量输出

            操作字符串

            操作时间

            条件判断

            遍历集合

            遍历时使用状态变量

            遍历map

            URL写法

            RESTful风格URL写法

            相关配置

    SpringBoot参数校验

            简单数据类型

            异常处理

            校验相关注解

            对象类型

    SpringTask

            定时任务

            入门案例

            Cron表达式

            @Schedule

            多线程任务


    部分图片来自百战程序员

    Spring缺点分析

    Spring是一个非常优秀的轻量级框架,以IOC(控制反转)和AOP(面向切面)为思想内核,极大简化了JAVA企业级项目的开发。

    虽然Spring的组件代码是轻量级的,但它的配置却是重量级的。使用Spring进行项目开发需要在配置文件中写很多代码,所有这些配置都代表了开发时的损耗。

    除此之外,Spring项目的依赖管理也是一件耗时耗力的事情。在环境搭建时,需要分析要导入哪些库的坐标,而且还需要分析导入与之有依赖关系的其他库的坐标,一旦选错了依赖的版本,随之而来的不兼容问题就会严重阻碍项目的开发进度。比如Spring5.0以上只能使用Junit4.12以上的版本。

    总结

    Spring的缺点:

    1. 配置过于繁琐。
    2. 引入的依赖过多,版本控制复杂。

    什么是SpringBoot

    SpringBoot对Spring的缺点进行改善和优化,基于约定大于配置的思想,简化了Spring的开发,所谓简化是指简化了Spring中大量的配置文件和繁琐的依赖引入。所以SpringBoot是一个服务于框架的框架,它不是对Spring功能的增强,而是提供了一种快速使用Spring框架的方式。

     

    SpringBoot的优点:

    1. 配置简单
    2. 依赖引入简单
    3. 提供了一些大型项目的非功能特性,如嵌入式服务器,安全指标,健康监测等。

    SpringBoot核心功能

    自动配置

    SpringBoot项目自动提供最优配置,同时可以修改默认值满足特定的要求。

    起步依赖

    SpringBoot的依赖是基于功能的,而不是普通项目的依赖是基于JAR包的。SpringBoot将完成一个功能所需要的所有坐标打包到一起,并完成了版本适配,我们在使用某功能时只需要引入一个依赖即可。

    SpringBoot项目结构

            POM文件

    接下来我们分析SpringBoot的项目结构:

    POM文件

    1. SpringBoot项目必须继承spring-boot-starter-parent,即所有的SpringBoot项目都是spring-boot-starter-parent的子项目。spring-boot-starter-parent中定义了常用配置、依赖、插件等信息,供SpringBoot项目继承使用。
      1. <parent>
      2.   <groupId>org.springframework.bootgroupId>
      3.   <artifactId>spring-boot-starter-parentartifactId>
      4.   <version>2.7.0-M1version>
      5.   <relativePath/>
      6. parent>

    2. SpringBoot项目中可以定义起步依赖,起步依赖不是以jar包为单位,而是以功能为单位
      1. <dependencies>
      2.   <dependency>
      3.     <groupId>org.springframework.bootgroupId>
      4.     <artifactId>spring-boot-starter-webartifactId>
      5.   dependency>
      6.   <dependency>
      7.     <groupId>org.springframework.bootgroupId>
      8.     <artifactId>spring-boot-starter-testartifactId>
      9.     <scope>testscope>
      10.   dependency>
      11. dependencies>

    3. spring-boot-maven-plugin插件是将项目打包成jar包的插件。该插件打包后的SpringBoot项目无需依赖web容器,可以直接使用JDK运行
      1. <build>
      2.   <plugins>
      3.     <plugin>
      4.       <groupId>org.springframework.bootgroupId>
      5.       <artifactId>spring-boot-maven-pluginartifactId>
      6.     plugin>
      7.   plugins>
      8. build>

            启动类、配置文件

    启动类

    启动类的作用是启动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

    SpringBoot入门案例

    1、通过IDEA搭建SpringBoot项目,点击新建项目,选中Spring Initializr,点击下一步 

    2、编写包结构,项目名称,项目类型等等 

    3、勾选起步依赖,然后创建项目

     4、在启动类同级包下或同级子包下创建控制器

    1. @Controller
    2. public class MyController {
    3. @RequestMapping("/hello")
    4. @ResponseBody
    5. public String hello(){
    6. System.out.println("hello");
    7. return "myController";
    8. }
    9. }

    5、运行启动类,访问对应路径 

    原理分析

            起步依赖

    查看spring-boot-starter-parent起步依赖

    1. 按住Ctrl点击pom.xml中的spring-boot-starter-parent,跳转到了spring-boot-starter-parentpom.xml,发现spring-boot-starter-parent的父工程是spring-boot-dependencies

    1. 进入spring-boot-dependenciespom.xml可以看到,一部分坐标的版本、依赖管理、插件管理已经定义好,所以SpringBoot工程继承spring-boot-starter-parent后已经具备版本锁定等配置了。所以起步依赖的作用就是进行依赖的传递。

    查看spring-boot-starter-web起步依赖

    按住Ctrl点击pom.xml中的spring-boot-starter-web,跳转到了spring-boot-starter-webpom.xml,从spring-boot-starter-webpom.xml中我们可以发现,spring-boot-starter-web就是将web开发要使用的spring-webspring-webmvc等坐标进行了打包,这样我们的工程只要引入spring-boot-starter-web起步依赖的坐标就可以进行web开发了,同样体现了依赖传递的作用。

            自动配置

    1. 查看注解@SpringBootApplication的源码

    @SpringBootConfiguration:等同与@Configuration,既标注该类是Spring的一个配置类
    @EnableAutoConfiguration:SpringBoot自动配置功能开启

     

    1. 查看注解@EnableAutoConfifiguration的源码

    @Import(AutoConfigurationImportSelector.class) 导入了AutoConfigurationImportSelector

     

    1. 查看AutoConfigurationImportSelector源码

    SpringFactoriesLoader.loadFactoryNames方法的作用就是从META-INF/spring.factories文件中读取指定类对应的类名称列表

     

    1. 点开spring-boot-autoconfigurespring.factories文件

    有关配置类的信息如下:

    上面配置文件存在大量的以Configuration为结尾的类名称,这些类就是存有自动配置信息的类,而SpringApplication在获取这些类名后再加载。

     

    1. 我们ServletWebServerFactoryAutoConfifiguration为例来分析源码:

    @EnableConfifigurationProperties(ServerProperties.class)代表加载ServerProperties服务器配置属性类。

     

    1. 进入ServerProperties类源码如下:

    prefifix = "server"表示SpringBoot配置文件中的前缀,SpringBoot会将配置文件中以server开始的属性映射到该类的字段中。所以配置网络端口的方式为server.port

     

    1. 如果我们没有在配置文件中配置默认端口,SpringBoot就会读取默认配置,而默认配置存放在META-INF/spring-configuration-metadata.json中,打开spring-boot-autoconfigurespring.factories文件

    该文件中保存的就是所有默认配置信息。

            核心注解

    @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。

    YAML文件

            配置文件介绍

    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,它的基本要求如下:

    1. 大小写敏感
    2. 使用缩进代表层级关系
    3. 相同的部分只出现一次

     

    比如使用properties文件配置tomcat端口:

    server.port=8888

    而使用YAML文件配置tomcat端口:

    server:

        port: 8888

            自定义配置简单数据

    除了覆盖默认配置,我们还可以在YAML文件中配置其他信息以便我们在项目中使用。配置简单数据的方式如下:

    • 语法:
      数据名:

     

    • 示例代码:

     

    注意:冒号后面必须有一个空格

            自定义配置对象数据

    • 语法:
      对象:
          属性名1: 属性值
          属性名2: 属性值
      # 或者
      对象: {属性名1: 属性值,属性名2: 属性值}
    • 示例代码:

    属性名前面的空格个数不限,在yml语法中,相同缩进代表同一个级别,只要每个属性前的空格数一样即可。

            自定义配置集合数据

    • 语法
      集合:
          -
      值1
          - 值2
      # 或者
      集合: [值1,值2]

     

    • 示例代码
      # 城市
    • # 集合中的元素是对象
    • 语法:
      集合名:
        - 对象属性名:

    注意:值与之前的 - 之间存在一个空格

            @Value读取配置文件数据

    我们可以通过@Value注解将配置文件中的值映射到一个Spring管理的Bean的字段上,用法如下:

    读取上次编写的文件数据

    application.yml:

    1. name: zhangsan
    2. student1:
    3. name: zhangsan
    4. age: 14
    5. student2: {name: zhangsan,age: 15}
    6. city1:
    7. - beijign
    8. - shanghai
    9. - tianjing
    10. city2: [beijing,shanghai,tianjing]
    11. students:
    12. - name: zhangsan
    13. age: 16
    14. sex: male
    15. - name: lisi
    16. age: 20
    17. sex: female
    18. - name: wangwu
    19. age: 25
    20. sex: male

     编写控制器:

    1. @Controller
    2. public class YmlController {
    3. @Value("${name}")
    4. private String name;
    5. @Value("${student1.age}")
    6. private int age;
    7. @Value("${city1[0]}")
    8. private String city1;
    9. @Value("${students[0].sex}")
    10. private String sex;
    11. @RequestMapping("/yml")
    12. @ResponseBody
    13. public String ymlController(){
    14. return name+":"+age+":"+city1+":"+sex;
    15. }
    16. }

    运行启动类,访问对应路径 

            @ConfigurationProperties

    @ConfigurationProperties(prefix="key")

    读取的类需要编写gettersetter方法,否则无法赋值
     

    application.yml:

    1. user:
    2. id: 10001
    3. username: shangxuetang
    4. address:
    5. - beijing
    6. - tianjin
    7. - shanghai
    8. - chongqing
    9. grades:
    10. - subject: math
    11. score: 100
    12. - subject: english
    13. score: 90

    编写控制器 

    1. package com.itbaizhan.springboot_blog.contoller;
    2. import com.itbaizhan.springboot_blog.domain.Grade;
    3. import org.springframework.boot.context.properties.ConfigurationProperties;
    4. import org.springframework.stereotype.Controller;
    5. import org.springframework.web.bind.annotation.RequestMapping;
    6. import org.springframework.web.bind.annotation.ResponseBody;
    7. import java.util.List;
    8. @Controller
    9. @ConfigurationProperties(prefix = "user")
    10. public class YmlController1 {
    11. private int id;
    12. private String username;
    13. private List address;
    14. private List grades;
    15. @RequestMapping("/yml2")
    16. @ResponseBody
    17. public String yml2(){
    18. System.out.println(id);
    19. System.out.println(username);
    20. System.out.println(address);
    21. System.out.println(grades);
    22. return "hello springboot!";
    23. }
    24. public int getId() {
    25. return id;
    26. }
    27. public void setId(int id) {
    28. this.id = id;
    29. }
    30. public String getUsername() {
    31. return username;
    32. }
    33. public void setUsername(String username) {
    34. this.username = username;
    35. }
    36. public List getAddress() {
    37. return address;
    38. }
    39. public void setAddress(List address) {
    40. this.address = address;
    41. }
    42. public List getGrades() {
    43. return grades;
    44. }
    45. public void setGrades(List grades) {
    46. this.grades = grades;
    47. }
    48. }

     

            占位符的作用

    YAML文件中可以使用${}占位符,它有两个作用:

    1、使用配置文件中的值

    1. 编写配置文件
      server:
        port: 8888
      myconfig:
        myport: ${server.port}
    2. 读取配置文件
      1. @Controller
      2. public class YmlController3{
      3.   @Value("${myconfig.myport}")
      4.   private int port;
      5.   @RequestMapping("/yml3")
      6.   @ResponseBody
      7.   public String yml3(){
      8.     System.out.println(port);
      9.     return "hello springboot!";
      10.    }
      11. }

    2、SpringBoot框架提供了一些生成随机数的方法可以在yml文件中使用:

    • ${random.value} :生成类似uuid的随机数,没有"-"连接
    • ${random.uuid} :生成一个uuid,有短杠连接
    • ${random.int} :随机取整型范围内的一个值
    • ${random.int(10)}:随机生成一个10以内的数
    • ${random.int(100,200)}:随机生成一个100-200 范围以内的数
    • ${random.long}:随机取长整型范围内的一个值
    • ${random.long(100,200)}:随机生成长整型100-200范围内的一个值

     

    用法如下:

    # 随机生成tomcat端口

    server:

      port: ${random.int(1024,9999)}

    SpringBoot访问静态资源

            静态资源相关目录

    SpringBoot项目中没有WebApp目录,只有src目录。在src/main/resources下面有statictemplates两个文件夹。SpringBoot默认在static目录中存放静态资源,而templates中放动态页面。

    static目录

    SpringBoot通过/resources/static目录访问静态资源,在resources/static中编写html页面:

    1. html>
    2. <html lang="en">
    3. <head>
    4. <title>测试htmltitle>
    5. head>
    6. <body>
    7. <h1>我的HTMLh1>
    8. <img src="/img/img.png">
    9. body>
    10. html>

     目录结构

    templates目录

    在SpringBoot中不推荐使用JSP作为动态页面,而是默认使用Thymeleaf编写动态页面。templates目录是存放Thymeleaf页面的目录,稍后我们讲解Thymeleaf技术。

            静态资源其他存放位置

    除了/resources/static目录,SpringBoot还会扫描以下位置的静态资源:

    • /resources/META‐INF/resources/
    • /resources/resources/
    • /resources/public/

    我们还可以在配置文件自定义静态资源位置

    在SpringBoot配置文件进行自定义静态资源位置配置

    spring:

      web:

       resources:

        static-locations: classpath:/suibian/,classpath:/static/

    注意:

    1. 该配置会覆盖默认静态资源位置,如果还想使用之前的静态资源位置,还需要配置在后面。
    2. SpringBoot2.5之前的配置方式为:spring.resources.static-locations

            重点:SpringBoot项目静态图片加载浏览器不显示解决方案

    SpringBoot项目静态图片加载浏览器不显示问题解决方案

    项目结构如下:

    我是通过Maven创建的以Thymeleaf为模板引擎创建的SpringBoot Web项目,发现加载的图片在浏览器不显示,本来我以为是配路径加载错误,后来发现路径并没有问题,我的图片放在src/main/resources/static/images目录中,在前端加载代码如下:${aBook.picture}是获取模型的图片名称。

     图书封面

    这样看起来其实没有任何问题,但是就是浏览器不显示图片,后面我以为需要像springMVC一样,需要配置静态资源路径,进行映射,防止拦截器拦截,我就在全局配置文件application.properties下配置了路径,后面发现根本没有必要配置,配置了也没有意义,因为SpringBoot默认访问static目录,所以最终的解决方法就是更新缓存并重启项目,就可以正常显示了。

    如下所示:

      

    Thymeleaf

            入门

    Thymeleaf是一款用于渲染XML/HTML5内容的模板引擎,类似JSP。它可以轻易的与SpringMVC等Web框架进行集成作为Web应用的模板引擎。在SpringBoot中推荐使用Thymeleaf编写动态页面。

    Thymeleaf最大的特点是能够直接在浏览器中打开并正确显示模板页面,而不需要启动整个Web应用。

    Thymeleaf在有网络和无网络的环境下皆可运行,它即可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。没有数据时,Thymeleaf的模板可以静态地运行;当有数据返回到页面时,Thymeleaf标签会动态地替换掉静态内容,使页面动态显示

    1. 创建Springboot项目
    2. 引入SpringMVC和Thymeleaf起步依赖
    1. <dependency>
    2. <groupId>org.springframework.bootgroupId>
    3. <artifactId>spring-boot-starter-thymeleafartifactId>
    4. dependency>
    5. <dependency>
    6. <groupId>org.springframework.bootgroupId>
    7. <artifactId>spring-boot-starter-webartifactId>
    8. dependency>

    创建视图index.html 

    1. html>
    2. <html lang="en" xmlns:th="http://www.thymeleaf.org">
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>thymeleaf入门title>
    6. head>
    7. <body>
    8. <h2 th:text="${msg}">helloh2>
    9. body>
    10. html>

     template中的html文件不能直接访问,需要编写Controller跳转到页面中

    1. @Controller
    2. public class PageController {
    3. // 页面跳转
    4. @GetMapping("/show")
    5. public String showPage(Model model){
    6. model.addAttribute("msg","Hello Thymeleaf");
    7. return "index";
    8. }
    9. }

     启动项目,访问 http://localhost:8080/show

    再访问静态页面 

            变量输出

    语法

    作用

    th:text

    将model中的值作为内容放入标签中

    th:value

    将model中的值放入input标签的value属性中

    1. 准备模型数据
      1. @GetMapping("/show")
      2. public String showPage(Model model){
      3.   model.addAttribute("msg","Hello Thymeleaf");
      4.   return"index";
      5. }
    2. 在视图展示model中的值
      1. html>
      2. <html lang="en" xmlns:th="http://www.thymeleaf.org">
      3. <head>
      4. <meta charset="UTF-8">
      5. <title>thymeleaf入门title>
      6. head>
      7. <body>
      8. <h2 th:text="${msg}">helloh2>
      9. <input th:value="${msg}">
      10. body>
      11. html>

            操作字符串

    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)}

    字符串转小写

    使用方式:

    1. html>
    2. <html lang="en" xmlns:th="http://www.thymeleaf.org">
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>thymeleaf入门title>
    6. head>
    7. <body>
    8. <h2 th:text="${msg}">helloh2>
    9. <input th:value="${msg}">
    10. <hr>
    11. <span th:text="${#strings.isEmpty(msg)}">span><br/>
    12. <span th:text="${#strings.substring(msg,2,5)}">span><br/>
    13. <span th:text="${#strings.contains(msg,'T')}">span><br/>
    14. body>
    15. html>

            操作时间

    操作时间的内置对象为dates

    方法

    说明

    ${#dates.format(key)}

    格式化日期,默认的以浏览器默认语言为格式化标准

    ${#dates.format(key,'yyyy/MM/dd')}

    按照自定义的格式做日期转换

    ${#dates.year(key)}

    取年

    ${#dates.month(key)}

    取月

    ${#dates.day(key)}

    取日

    1. 准备数据
      model.addAttribute("date",newDate(130,01,01));

    2. 使用内置对象操作时间
      1. <span th:text="${#dates.format(date)}">span>
      2. <span th:text="${#dates.format(date,'yyyy/MM/dd')}">span>
      3. <span th:text="${#dates.year(date)}">span>
      4. <span th:text="${#dates.month(date)}">span>
      5. <span th:text="${#dates.day(date)}">span>

            条件判断

    语法

    作用

    th:if

    条件判断

    1. 准备数据
      model.addAttribute("sex","女");
    2. 进行条件判断
      1. <div>
      2.   <span th:if="${sex} == '男'">
      3.      性别:男
      4.   span>
      5.   <span th:if="${sex} == '女'">
      6.      性别:女
      7.   span>
      8. div>

    语法

    作用

    th:switch/th:case

    th:switch/th:case与Java中的switch语句等效。th:case="*"表示Java中switch的default,即没有case的值为true时显示th:case="*"的内容。

    1. 准备数据,进行条件判断
      model.addAttribute("id","12");
      1. <div th:switch="${id}">
      2. <span th:case="1">1span>
      3. <span th:case="2">2span>
      4. <span th:case="3">3span>
      5. <span th:case="*">*span>
      6. div>

            遍历集合

    语法

    作用

    th:each

    迭代器,用于循环迭代集合

    遍历集合

            编写实体类

    1. public class Users{
    2. private String id;
    3. private String name;
    4. private int age;
    5. // 省略getter/setter/构造方法
    6. }

            准备数据

    1. List users=new ArrayList<>();
    2. users.add(new Users("1","sxt",23));
    3. users.add(new Users("2","baizhan",22));
    4. users.add(new Users("3","admin",25));
    5. model.addAttribute("users",users);

            在页面中展示数据

    1. <table border="1" width="50%">
    2. <tr>
    3. <th>IDth>
    4. <th>Nameth>
    5. <th>Ageth>
    6. tr>
    7. <tr th:each="user : ${users}">
    8. <td th:text="${user.id}">td>
    9. <td th:text="${user.name}">td>
    10. <td th:text="${user.age}">td>
    11. tr>
    12. table>

            遍历时使用状态变量

    thymeleaf将遍历的状态变量封装到一个对象中,通过该对象的属性可以获取状态变量:

    状态变量

    含义

    index

    当前迭代器的索引,从0开始

    count

    当前迭代对象的计数,从1开始

    size

    被迭代对象的长度

    odd/even

    布尔值,当前循环是否是偶数/奇数,从0开始

    first

    布尔值,当前循环的是否是第一条,如果是返回true,否则返回false

    last

    布尔值,当前循环的是否是最后一条,如果是则返回true,否则返回false

    使用状态变量

    1. <tr th:each="user,status : ${users}">
    2.   <td th:text="${user.id}">td>
    3.   <td th:text="${user.name}">td>
    4.   <td th:text="${user.age}">td>
    5.   <td th:text="${status.index}">td>
    6.   <td th:text="${status.count}">td>
    7.   <td th:text="${status.size}">td>
    8.   <td th:text="${status.odd}">td>
    9.   <td th:text="${status.even}">td>
    10.   <td th:text="${status.first}">td>
    11.   <td th:text="${status.last}">td>
    12. tr>

            遍历map

    遍历出的是一个键值对对象,key获取键,value获取值

    1. 准备数据
    2. Map map=new HashMap<>();
    3. map.put("user1",new Users("1","shangxuetang",23));
    4. map.put("user2",new Users("2","baizhan",22));
    5. map.put("user3",new Users("3","admin",25));
    6. model.addAttribute("map",map);
    1. 遍历map
    2. <table border="1" width="50%">
    3.   <tr>
    4.     <th>IDth>
    5.     <th>Nameth>
    6.     <th>Ageth>
    7.     <th>Keyth>
    8.   tr>
    9.  
    10.   <tr th:each="m : ${map}">
    11.     <td th:text="${m.value.id}">td>
    12.     <td th:text="${m.value.name}">td>
    13.     <td th:text="${m.value.age}">td>
    14.     <td th:text="${m.key}">td>
    15.   tr>
    16. table>

            获取域中的数据

    thymeleaf也可以获取request,session,application域中的数据,方法如下:

    准备数据

    1. request.setAttribute("req","HttpServletRequest");
    2. session.setAttribute("ses","HttpSession");
    3. session.getServletContext().setAttribute("app","application");

     获取域数据

    1. request1: <span th:text="${#request.getAttribute('req')}"/>
    2. request2:<span th:text="${#httpServletRequest.getAttribute('req')}"/>
    3. <hr/>
    4. session1: <span th:text="${session.ses}"/>
    5. session2: <span th:text="${#httpSession.getAttribute('ses')}"/>
    6. <hr/>
    7. application1: <span th:text="${application.app}"/>
    8. application2:<span th:text="${#servletContext.getAttribute('app')}"/>

            URL写法

    在Thymeleaf中路径的写法为@{路径}

    th:href="@{http://www.baidu.com}">百度

    在路径中添加参数

    1. 准备数据
      1. model.addAttribute("id","100");
      2. model.addAttribute("name","bzcxy");

    2. 准备跳转后访问的Controller
      1. @GetMapping("/show2")
      2. @ResponseBody
      3. public String show2(String id,String name){
      4.   return id+":"+name;
      5. }

    3. 在URL中添加参数
      1. <a th:href="@{show2?id=1&name=sxt}">静态参数一a>
      2. <a th:href="@{show2(id=2,name=bz)}">静态参数二a>
      3. <a th:href="@{'show2?id='+${id}+'&name='+${name}}">动态参数一a>
      4. <a th:href="@{show2(id=${id},name=${name})}">动态参数二a>

            RESTful风格URL写法

    1. 准备跳转后访问的Controller
      1. @GetMapping("/show3/{id}/{name}")
      2. @ResponseBody
      3. public String show3(@PathVariable String id,@PathVariable String name){
      4.   return id+":"+name;
      5. }

    2. 在URL中添加参数
      <a th:href="@{/show3/{id}/{name}(id=${id},name=${name})}">restful格式传递参数方式a>

            相关配置

    在Springboot配置文件中可以进行Thymeleaf相关配置

    配置项

    含义

    spring.thymeleaf.prefix

    视图前缀

    spring.thymeleaf.suffix

    视图后缀

    spring.thymeleaf.encoding

    编码格式

    spring.thymeleaf.servlet.content-type

    响应类型

    spring.thymeleaf.cache=false

    页面缓存,配置为false则不启用页面缓存,方便测试

    1. spring:
    2.   thymeleaf:
    3.    prefix: classpath:/templates/
    4.    suffix: .html
    5.    encoding: UTF-8
    6.    cache: false
    7.    servlet:
    8.     content-type: text/html

    SpringBoot参数校验

            简单数据类型

    SpringBoot自带了validation工具可以从后端对前端传来的参数进行校验,

    用法如下:

    在类上方添加@Validated注解

    在参数前添加@NotBlank注解

     

    引入validation起步依赖

    1. <dependency>
    2. <groupId>org.springframework.bootgroupId>
    3. <artifactId>spring-boot-starter-validationartifactId>
    4. dependency>

    编写Controller 

    1. // 该控制器开启参数校验
    2. @Validated
    3. @Controller
    4. public class TestController{
    5. @RequestMapping("/t1")
    6. @ResponseBody
    7. // 在参数前加校验注解,该注解的意思是字符串参数不能为null
    8. public String t1(@NotBlank String username){
    9. System.out.println(username);
    10. return "请求成功!";
    11. }
    12. }

    访问 http://localhost:8080/t1

    如果没有传递参数则会报异常 

    在校验参数的注解中添加message属性,可以替换异常信息。 

    1. // 该控制器开启参数校验
    2. @Validated
    3. @Controller
    4. public class TestController{
    5. @RequestMapping("/t1")
    6. @ResponseBody
    7. // 在参数前加校验注解,该注解的意思是字符串参数不能为null
    8. public String t1(@NotBlank(message="用户名不能为空") String username){
    9. System.out.println(username);
    10. return "请求成功!";
    11. }
    12. }

     

            异常处理

    当抛出ConstraintViolationException异常后,我们可以使用SpringMVC的异常处理器,也可以使用SpringBoot自带的异常处理机制。

    当程序出现了异常,SpringBoot会使用自带的BasicErrorController对象处理异常。该处理器会默认跳转到/resources/templates/error.html页面

    编写异常页面:

    1. html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>错误页面title>
    6. head>
    7. <body>
    8. <h1>服务器开小差了!h1>
    9. body>
    10. html>

    我们再次访问t2并且不传递参数 

            校验相关注解

    SpringBoot参数校验_校验相关注解

    注解

    作用

    @NotNull

    判断包装类是否为null

    @NotBlank

    判断字符串是否为null或者是空串(去掉首尾空格)

    @NotEmpty

    判断集合是否为空

    @Length

    判断字符的长度(最大或者最小)

    @Min

    判断数值最小值

    @Max

    判断数值最大值

    @Email

    判断邮箱是否合法

    1. @RequestMapping("/t2")
    2. @ResponseBody
    3. public String t2(
    4.     @NotBlank @Length(min=1, max=5) String username,
    5.     @NotNull @Min(0) @Max(150) Integer age,
    6.     @NotEmpty @RequestParamList address,
    7.     @NotBlank @Email String email) {
    8.   System.out.println(username);
    9.   System.out.println(age);
    10.   System.out.println(address);
    11.   System.out.println(email);
    12.   return "请求成功!";
    13. }

            对象类型

    校验的对象参数前添加@Validated,并将异常信息封装到BindingResult对象

    SpringBoot也可以校验对象参数中的每个属性,用法如下:

    1. 添加实体类
    2. public class Student{
    3.   @NotNull(message="id不能为空")
    4.   private Integer id;
    5.   @NotBlank(message="姓名不能为空")
    6.   private String name;
    7.   // 省略getter/setter/tostring
    8. }
    9. 编写控制器
    10. @Controller
    11. public class TestController2{
    12.   @RequestMapping("/t3")
    13.   @ResponseBody
    14.  

      // 校验的对象参数前添加@Validated,并将异常信息封装到BindingResult对象

    1.   public String t3(@Validated Student student,BindingResult result) {
    2.     // 判断是否有参数异常
    3.     if(result.hasErrors()) {
    4.       // 所有参数异常
    5.       List list = result.getAllErrors();
    6.       // 遍历参数异常,输出异常信息
    7.       for(ObjectError err :  list) {
    8.         FieldError fieldError = (FieldError) err;
    9.         System.out.println(fieldError.getDefaultMessage());
    10.        }
    11.       return "参数异常";
    12.      }
    13.     System.out.println(student);
    14.     return "请求成功!";
    15.    }
    16. }

    SpringTask

            定时任务

    定时任务即系统在特定时间执行一段代码,它的场景应用非常广泛:

    1. 购买游戏的月卡会员后,系统每天给会员发放游戏资源。
    2. 管理系统定时生成报表。
    3. 定时清理系统垃圾。
    4. ......

    定时任务的实现主要有以下几种方式:

    1. Java自带的java.util.Timer类,这个类允许调度一个java.util.TimerTask任务。使用这种方式可以让程序按照某一个频度执行,但不能在指定时间运行。一般用的较少。
    2. Quartz。这是一个功能比较强大的的调度器,可以让程序在指定时间执行,也可以按照某一个频度执行,配置起来稍显复杂。
    3. Spring3.0以后自带Spring Task,可以将它看成一个轻量级的Quartz,使用起来比 Quartz简单许多,在课程中我们使用Spring Task实现定时任务

     

            入门案例

    1、创建SpringBoot项目,

    启动类上方添加@EnableScheduling注解开启定时任务。

     

    2、编写定时任务类

    方法上方添加 @Scheduled ,将该方法设置为定时方法,并且需要将定时类放到Spring容器中

    1. @Component
    2. public class Task1 {
    3. @Scheduled(cron = "* * * * * *")
    4. public void t1(){
    5. SimpleDateFormat sdf = new SimpleDateFormat("HH-mm-ss");
    6. System.out.println(sdf.format(new Date()));
    7. }
    8. }

     

    3、启动项目,定时任务方法按照配置定时执行。

     

            Cron表达式

    Spring Task依靠Cron表达式配置定时规则。Cron表达式是一个字符串,分为6或7个域,每一个域代表一个含义,以空格隔开。有如下两种语法格式:

    1. Seconds Minutes Hours DayofMonth Month DayofWeek Year
    2. Seconds Minutes Hours DayofMonth Month DayofWeek

    Seconds(秒):域中可出现, - * /四个字符,以及0-59的整数

    • *:表示匹配该域的任意值,在Seconds域使用*,表示每秒钟都会触发
    • ,:表示列出枚举值。在Seconds域使用5,20,表示在5秒和20秒各触发一次。
    • -:表示范围。在Seconds域使用5-20,表示从5秒到20秒每秒触发一次
    • /:表示起始时间开始触发,然后每隔固定时间触发一次。在Seconds域使用5/20, 表示5秒触发一次,25秒,45秒分别触发一次。

    Minutes(分),Hours(时)同上

     

    DayofMonth(日期):域中可出现, - * / ? L W C八个字符,以及1-31的整数

    • C:表示和当前日期相关联。在DayofMonth域使用5C,表示在5日后的那一天触发,且每月的那天都会触发。比如当前是10号,那么每月15号都会触发。
    • L:表示最后,在DayofMonth域使用L,表示每个月的最后一天触发。
    • W:表示工作日,在DayofMonth域用15W,表示最接近这个月第15天的工作日触发,如果15号是周六,则在14号即周五触发;如果15号是周日,则在16号即周一触发;如果15号是周二则在当天触发。
      注:
    1. 该用法只会在当前月计算,不会到下月触发。比如在DayofMonth域用31W,31号是周日,那么会在29号触发而不是下月1号。
    2. 在DayofMonth域用LW,表示这个月的最后一个工作日触发。

     

    Month(月份):域中可出现, - * /四个字符,以及1-12的整数或JAN-DEC的单词缩写

    DayofWeek(星期):可出现, - * / ? L # C八个字符,以及1-7的整数或SUN-SAT 单词缩写,1代表星期天,7代表星期六

    • C:在DayofWeek域使用2C,表示在2日后的那一天触发,且每周的那天都会触发。比如当前是周一,那么每周三都会触发。
    • L :在DayofWeek域使用L,表示在一周的最后一天即星期六触发。在DayofWeek域使用5L,表示在一个月的最后一个星期四触发。
    • #:用来指定具体的周数,#前面代表星期几,#后面代表一个月的第几周,比如5#3表示一个月第三周的星期四。
    • ?:在无法确定是具体哪一天时使用,用于DayofMonth和DayofWeek域。例如在每月的20日零点触发1次,此时无法确定20日是星期几,写法如下:0 0 0 20 * ?;或者在每月的最后一个周日触发,此时无法确定该日期是几号,写法如下:0 0 0 ? * 1L

    Year(年份):域中可出现, - * /四个字符,以及1970~2099的整数。该域可以省略,表示每年都触发。

     

            @Schedule

    @Scheduled写在方法上方,指定该方法定时执行。常用参数如下:

    • cron:cron表达式,定义方法执行的时间规则。
    • fixedDelay:任务立即执行,之后每隔多久执行一次,单位是毫秒,上一次任务结束后计算下次执行的时间
    • fixedRate:任务立即执行,之后每隔多久执行一次,单位是毫秒,上一次任务开始后计算下次执行的时间 
    • initialDelay:项目启动后不马上执行定时器,根据initialDelay的值延时执行。

     

            多线程任务

    Spring Task定时器默认是单线程的,如果项目中使用多个定时器,使用一个线程会造成效率低下。代码如下:

    1. @Component
    2. public class Task1 {
    3. SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
    4. @Scheduled(cron = "* * * * * *")
    5. public void t1(){
    6. try {
    7. Thread.sleep(5000);
    8. } catch (InterruptedException e) {
    9. e.printStackTrace();
    10. }
    11. System.out.println(Thread.currentThread().getId()+":线程1,时间:"+sdf.format(new Date()));
    12. }
    13. @Scheduled(cron = "* * * * * *")
    14. public void t2(){
    15. System.out.println(Thread.currentThread().getId()+":线程2,时间:"+sdf.format(new Date()));
    16. }
    17. }

     

    任务1较浪费时间,会阻塞任务2的运行。此时我们可以给Spring Task配置线程池。

    通过配置类实现SchedulingConfigurer接口,重写方法

     

    1. @Configuration
    2. public class SchedulingConfig implements SchedulingConfigurer {
    3. @Override
    4. public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
    5. //创建线程池
    6. taskRegistrar.setScheduler(Executors.newScheduledThreadPool(5));
    7. }
    8. }

    此时任务1不会阻塞任务2的运行

  • 相关阅读:
    单片机上的操作系统
    Client引入Eureka报Completed shut down of DiscoveryClient问题原因及解决方式
    js对象扁平化:Javascript对象进行扁平化处理
    【Verilog 教程】7.1Verilog 除法器设计
    第一章 初识debug
    深度学习系列1——Pytorch 图像分类(LeNet)
    android 12.0 去掉未知来源弹窗 默认授予安装未知来源权限
    使用 Stable Diffusion Img2Img 生成、放大、模糊和增强
    记录一次SQL函数和优化的问题
    互联网云厂商大转向:在海外重燃新「战事」
  • 原文地址:https://blog.csdn.net/cccccccmmm/article/details/128068720