SpringBoot

1、SpringBoot简介

Spring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化新 Spring 应用的初始搭建以及开发过程。该框架使用了特定的方式进行配置,从而使开发人员不再需要定义样板化的配置。用我的话来理解,就是 Spring Boot 其实不是什么新的框架,它默认配置了很多框架的使用方式,就像 Maven 整合了所有的 Jar 包,Spring Boot 整合了所有的框架。

Spring Boot是一个快速的开发框架,能够帮助程序员快速整合第三方框架,内置了第三方容器(tomcat/jetty/undertom),完全简化编写xml,采用是注解方式。

优势:

  • 快速构建项目
  • 对主流开发框架的无配置集成
  • 项目可独立运行,无须外部依赖Servlet容器
  • 提供运行时的应用监控
  • 极大的提高了开发、部署效率
  • 与云计算的天然集成

2、第一个SpringBoot项目

2.1maven形式创建

2.1.1创建maven工程

选择maven,直接next

点击next,填写相应信息:

点击finish。

2.1.2导入依赖

<!--    导入springboot版本和框架依赖     -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.5.1</version>
    <relativePath></relativePath>
</parent>
<!--    导入动态web场景启动器    -->
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>
<!--添加maven插件,项目的打包工具,打成jar包,否则在打包运行时报错   -->
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

2.1.3创建springboot启动类

创建包com.tjetc,在包下创建启动类

创建启动类

package tjetc;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * 启动类
 */
@SpringBootApplication
public class HelloApplication {
    public static void main(String[] args) {
        SpringApplication.run(HelloApplication.class, args);
    }
}

2.1.4创建controller

创建com.tjetc.controller包,在包下创建controller类

package tjetc.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class HelloController {
    @RequestMapping("/hello")
    @ResponseBody
    public String hello() {
        return "Hello Spring Boot!";
    }
}

2.1.5启动springBoot

运行启动类的main方法

启动成功,端口号8080

2.1.6访问controller

在resources下创建配置文件,application.properties,springboot启动以后,默认读取该配置文件。配置端口号,contextpath

#配置web启动端口
server.port=8081
#配置web上下文路径
server.servlet.context-path=/first

访问:

2.1.7打包部署

Springboot工程被打包成jar包,通过package命令打成jar包。

将当前的工程打成jar包

放在target下面。

/usr/local >java -jar jar包名称

Dfsd> java -jar /usr/local/jar包的名称

在当前的路径下打开cmd窗口。运行jar包:

访问controller

2.2引导器快速创建

创建project

点击next:填写group和Artifact,确定打包方式,jdk版本,和package表示的启动类所在的包名,如果连接失败,可以使用Custom:阿里的镜像服务Custom:https://start.aliyun.com

没有问题,点击next。选择依赖:创建web工程,只要选择web依赖即可,选择springboot版本,选择当前稳定版本。点击next。

(1)官方

(2)阿里

确定module信息以后,点击finish

创建工程以后,可以删掉一些不用的(下面选中的):

  • 自动创建了包:com.tjetc,并在包下创建了启动类和测试类。
  • 同时在resources下创建了static,templates和springboot的主配置文件application.properties。
  • 在启动类启动时,会自动读取主配置文件中的数据。Springboot内部集成了tomcat,默认端口号8080,可以在主配置文件中指定端口号。
  • 在pom.xml中导入了parent,web依赖和单元测试,以及打包工具。

2.3理解Pom文件的依赖与starter启动器的作用

2.3.1Pom文件的依赖

Pom文件中的parent是Spring Boot的框架版本控制中心

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.5.1</version>
    <relativePath></relativePath>
</parent>

spring-boot-starter-parent点进去看一下parent是如何控制版本的。点进去之后,也有一个父工程

父工程为spring-boot-dependencies,看到名字应该想到,是springboot的依赖。同时还有其他东西。再点进去,看到里面有<dependencyManagement>,管理各种依赖的版本。

到这里就理解了parent是如何控制版本的。我们创建的springboot工程是spring-boot-start-parent的子工程,spring-boot-starter-parent是spring-boot-denpendencies的子工程,父工程通过dependencyManagement控制了各种依赖的版本。所以当子工程导入依赖时,可以不写版本,自动使用父工程规定的版本。以此来进行版本的控制。

2.3.2spring-boot场景启动器

starter:spring-boot场景启动器,以web启动器为例:

<!--    导入动态web场景启动器    -->
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

点进去看到spring-boot-starter-web的内容:

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <version>2.5.1</version>
    <scope>compile</scope>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-json</artifactId>
    <version>2.5.1</version>
    <scope>compile</scope>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    <version>2.5.1</version>
    <scope>compile</scope>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>5.3.8</version>
    <scope>compile</scope>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.8</version>
    <scope>compile</scope>
  </dependency>
</dependencies>

Spring-boot-starter-web导入了web环境所有的依赖,只需导入starter,可自动导入web模块正常运行所依赖的组件。其他的starter也是一样的。

springboot出厂默认就写好了很多starter,如:

spring-boot-starter-activemq,spring-boot-starter-aop,spring-boot-starter-data-redis,spring-boot-starter-data-solr等

重要提示:Spring Boot将所有的绝大部分框架整合场景都进行了抽取,做成一个个的starters(启动器),只需要在项目里面引入这些starter相关整合所需的依赖都会导入进来。

2.4理解主程序@SpringBootApplication

@SpringBootApplication 用于标识spring boot应用程序,代表该类是一个spring boot启动类

Spring boot运行这个类的main方法时启动SpringBoot应用。

@SpringBootConfiguration: Spring Boot的配置类。标注在类上表示是一个Spring Boot的配置类

@ComponentScan:根据定义的扫描路径,把符合扫描规则的类装配到spring容器中

虽然起启动类没有放在根包,可以通过ComponentScan注解重新定义扫描的包,不在使用springboot默认扫描启动类所在包和子包

@Configuration:配置类上来标注这个注解。配置类相当于配置文件。配置类也是容器中的一个组件。

@Component把组件实例化到spring容器中。

@EnableAutoConfiguration:开启自动配置功能;

当我们需要Spring Boot帮我们自动配置所需要的配置,@EnableAutoConfiguration告诉Spring Boot开启自动配置功能,这样Spring Boot会自动配置好并使之生效。

3SpringBoot配置文件

3.1学会Spring Boot全局配置和yaml的语法

Spring Boot全局配置文件(在src/main/resources目录或者类路径/config下),名称如下:

         application.properties

         application.yaml/yml

也许作者认为properties或json 的写法不爽,于是发明了yml这种以数据为中心写法的配置文件。

yml是YAML(YAML Ain't Markup Language)语言的文件,以数据为中心,比json、xml等更适合做配置文件。

3.1.1语法

参考语法规范:http://www.yaml.org

语法校验 : https://nodeca.github.io/js-yaml

YAML基本语法

A.使用缩进表示层级关系

B.缩进时不允许使用Tab键,只允许使用空格。

C.缩进的空格数目不重要,只要相同层级的元素左侧对齐即可

D.大小写敏感

E.键与值之间一定要有空格

3.1.2yml支持的三种数据的结构

YAML 支持的三种数据结构

  • 常见普通值:单个的、不可再分的值
  • 对象:键值对的集合
  • 数组:一组按次序排列的值

1.单个的,不能再分割的值

#单个值,不能再分割的值
name: rose

2.对象:键值对的集合

(1)第一种写法

#对象,键值对的集合
person:
  #属性名: 属性值
  name: kelly
  age: 21
  sex: female

(2)第二种写法(将对象写在一行上,键与值之间也要有空格)

user: {name: jack,age: 21,sex: male}

3.数组:一组按次序排列的值,-与值之间也要空格

(1)第一种写法

#数组或集合,一组按次序排列的值
names:
  - kelly
  - jack
  - jim

(2)第二种写法(将对象写在一行上)

ages: [21,21,20]

4、数组中存的是对象

(1)第一种写法(name和age的左侧要对齐)

people:
  - name: kelly
    age: 21
    sex: female
  - name: marry
    age: 20
    sex: female

(2)第二种写法

users:
  - {name: jack,age: 21,sex: male}
  - {name: jim,age: 20,sex: male}

5、对象有数组

#对象有数组
student:
  lists: [21,21,20]
  abc: 
    - 20
    - 20
    - 20

3.2Spring Boot获取配置文件的值及配置文件编码设置

3.2.1创建实体类

package com.jetc.entity;
public class Book {
    private String bookName;
    private String author;
    public String getBookName() {return bookName;}
    public void setBookName(String bookName) {this.bookName = bookName;}
    public String getAuthor() {return author;}
    public void setAuthor(String author) {this.author = author;}
    @Override
    public String toString() {
        return "Book{" +"bookName='" + bookName + '\'' + ", author='" + author + '\'' +'}';
    }
}
package com.jetc.entity;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Component;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
/*将student对象放入容器
    springboot容器创建student的bean对象并管理起来
*/
@Component
/*(1)读取主配置文件(application.properties或者
    application.yaml或者application.yml)中的数据,
    赋值给student对象(将数据注入到springboot管理的student对象的属性中)
  (2)需要属性,指定主配置文件中的数据
    prefix = "student1” 表示在配置中第一个层级是student1
*/
@ConfigurationProperties(prefix = "student1")
public class Student {
    private String studentName;
    private Integer age;
    private Boolean sex;//男代表true
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate birth;
    private Map<String, Object> maps;
    private List<Object> lists;
    private Book book;
    public String getStudentName() {return studentName;}
    public void setStudentName(String studentName) {this.studentName = studentName;}
    public Integer getAge() {return age;}
    public void setAge(Integer age) {this.age = age;}
    public Boolean getSex() { return sex;}
    public void setSex(Boolean sex) {this.sex = sex;}
    public LocalDate getBirth() { return birth; }
    public void setBirth(LocalDate birth) {this.birth = birth; }
    public Map<String, Object> getMaps() {return maps;}
    public void setMaps(Map<String, Object> maps) {this.maps = maps;}
    public List<Object> getLists() { return lists; }
    public void setLists(List<Object> lists) { this.lists = lists;}
    public Book getBook() { return book; }
    public void setBook(Book book) {this.book = book; }
    @Override
    public String toString() {
        return "Student{" +
                "studentName='" + studentName + '\'' +
                ", age=" + age +
                ", sex=" + sex +
                ", birth=" + birth +
                ", maps=" + maps +
                ", lists=" + lists +
                ", book=" + book +
                '}';
    }
}

3.2.2在主配置文件中配置student

#配置student  
#@ConfigurationProperties    studentName-->student-name或者studentName
student1:
  studentName: 猪猪儿
  age: 21
  sex: false
  birth: 2000-09-05
  map1:
    k1: v1
    k2: v2
  lists:
    - zhangsan
    - lisi
    - wangwu
  book:
    bookName: 红岩
    author: 罗广斌

3.2.3编写测试类

package com.jetc;
import com.jetc.entity.Student;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest//使用springboot环境测试
class ApplicationTests {
    @Autowired
    private Student student;
    @Test
    void contextLoads() { System.out.println(student);}
}

3.2.4执行结果

Student{studentName='猪猪儿', age=21, sex=false, birth=2000-09-05, maps=null, lists=[zhangsan, lisi, wangwu], book=Book{bookName='红岩', author='罗广斌'}}

启动springboot测试类,会自动加载主配置文件,@ConfigurationProperties负责将主配置文件中的数据赋值给student对象。Student对象已经通过@Component注解注册到容器中,所以在测试类中可以直接注入。

@ConfigurationProperties 映射实体的属性值

可以为实体对读入配置文件的值,支持所有类型值的读取;

前提是实体类需要提供一个setter或使用可变的值初始化它.

 

先把之前的application.yml放到别的包下

在application.properties中配置student1

#配置student1
student1.student-name=猪猪儿
student1.age=21
student1.sex=false
student1.birth=2000-09-05
student1.maps.k1=v1
student1.maps.k2=v2
student1.lists=zhangsan,lisi,wangwu
student1.book.bookName=红岩
student1.book.author=杨益言

最终执行结果:

Student{studentName='猪猪儿', age=21, sex=false, birth=2000-09-05, maps={k1=v1, k2=v2}, lists=[zahngsan, lisi, wangwu], book=Book{bookName='红岩', author='杨益言'}}

在使用properties文件的时候,中文乱码。如何解决?修改properties文件的编码,将所有编码类型都设置为utf-8

都改好以后,点击OK。

原来的application.properties就乱码了,修改乱码,重新运行,乱码问题解决了

Student{studentName='猪猪儿', age=21, sex=false, birth=2000-09-05, maps={k1=v1, k2=v2}, lists=[zhangsan, lisi, wangwu], book=Book{bookName='红岩', author='杨益言'}}

3.3@ConfigurationProperties与@Value区别

松散语法属性名匹配规则:

  • student.studentName:使用标准方式
  • student.student-name:小写写用-
  • student. STUDENT_NAME:大写用_

3.3.1功能不同

  • @ConfigurationProperties是可以批量注入的,一次搞定
  • @Value只能一个一个注入,如下:

3.3.1松散绑定

@ConfigurationProperties支持松散绑定studentName可以写为student-name都没有问题。

@Value不可以。@Value进行绑定时,名称只能与application.properties中属性的名称相同

3.3.2SpEL

@ConfigurationProperties不支持SpEL,比如:

Yml文件配置不支持SpEL

不会报错,但是不能注入到studentage

@Value支持SpEL。比如:\

Age是可以正常注入的。

3.3.3复杂类型封装

@ConfigurationProperties支持复杂类型封装

@Value不支持复杂类型,只支持字符串,和基本数据类型及其包装类。

3.4 @PropertySource@Bean@Configuration@Import

3.4.1@PropertySource

  • 读取指定的properties配置文件
  • 如果我们将student的数据写在任意一个配置文件中,比如。student.properties文件中,如何读取配置中信息,注入到student中。可以使用该注解
  • 复杂属性文件内部的属性的获取,如果不通过@PropertySource指定属性文件,默认读取的就是application.properties或者application.yml文件

3.4.2@Bean、@Configuration

Spring Boot 由于没有XML文件,所以所有的Bean管理都放入在一个配置类中实现。

配置类就是类上具有@Configuration的类。这个类就相当于之前的applicationContext.xml

配置类中的bean注入到容器

写一个config包,用来存放配置类:

package com.tjetc.service;
public class UserService {
    public UserService() {
        System.out.println("UserService的无参构造方法");
    }
    public void add() {
        System.out.println("UserService的add方法");
    }
}

创建配置类:

package com.tjetc.config;
import com.tjetc.service.UserService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/*
    代表当前类是一个配置类,可以达到跟配置文件相同的效果
    创建一个userService的对象,放在容器中
*/
@Configuration
public class MyConfig {
    /*
    springboot启动过程中调用有@Bean注解的方法,生成bean对象交给springboot容器管理
    @Configuration+@Bean相当于spring.xml中<bean>标签的作用:
        将方法的返回值存入spring容器,bean对象的id是方法名
    */
    @Bean
    public UserService userService() {
        return new UserService();
    }
}

测试:

package com.tjetc;
import com.tjetc.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class ApplicationTests {
    @Autowired
    private UserService userService;
    @Test
    void contextLoads() {
        userService.add();
    }
}

结果:

如果@Bean对应的方法是非静态方法,那么只执行一次

package com.tjetc;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
    public void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
package com.tjetc.config;
import com.tjetc.service.UserService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
@Configuration
public class MyConfig {
    @Bean
    public UserService userService() {
        return new UserService();
    }
    @Bean
    public String abc() {
        System.out.println("MyConfig的abc方法");
        userService();
        userService();
        userService();
        userService();
        userService();
        return "aaa";
    }
}

如果是静态方法,执行多次,生成的bean是多例的

package com.tjetc.config;
import com.tjetc.service.UserService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

@Configuration
public class MyConfig {
    @Bean
    public static UserService userService() {
        return new UserService();
    }
    @Bean
    public static String abc() {
        System.out.println("MyConfig的abc方法");
        userService();
        userService();
        userService();
        userService();
        userService();
        return "aaa";
    }
}
package com.tjetc;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

3.4.3@Import

@Import可以引入一个或多个类型,代表将该类实例化到IOC容器中。可以用在启动类上,或者配置类上,只能在程序启动的时候,能够读取该注解即可,这样该注解表示的类型就会被实例化到容器中。

创建db.properties属性文件

jdbc.driverName=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
jdbc.username=root
jdbc.password=123456

创建DbConfiguration类,读取db.properties属性文件,并为属性赋值。

 (1)使用@Value

package com.tjetc.common;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
/*
指定读取的属性文件,这里仅是读取属性文件,赋值,并不会把MyDataSource实例化到容器当中
*/
@PropertySource("classpath:db.properties")
public class DbConfiguration {
    /*指定属性文件中的相应属性*/
  @Value("${jdbc.driverName}")
    private String driverName;
  @Value("${jdbc.url}")
    private String url;
  @Value("${jdbc.username}")
    private String username;
  @Value("${jdbc.password}")
    private String password;
    public String getDriverName() {return driverName; }
    public void setDriverName(String driverName) {this.driverName = driverName;}
    public String getUrl() { return url; }
    public void setUrl(String url) {this.url = url;}
    public String getUsername() {return username;}
    public void setUsername(String username) {this.username = username;}
    public String getPassword() {return password;}
    public void setPassword(String password) {this.password = password;}
    @Override
    public String toString() {
        return "DbConfiguration{" +
                "driverName='" + driverName + '\'' +
                ", url='" + url + '\'' +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

(2)使用@ConfigurationProperties(prefix = "jdbc")

package com.tjetc.common;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
/*
指定读取的属性文件,这里仅是读取属性文件,赋值,并不会把MyDataSource实例化到容器当中
*/
@PropertySource("classpath:db.properties")
@ConfigurationProperties(prefix = "jdbc")
public class DbConfiguration {
    private String driverName;
    private String url;
    private String username;
    private String password;
    public String getDriverName() {return driverName;}
    public void setDriverName(String driverName) {this.driverName = driverName;}
    public String getUrl() {return url;}
    public void setUrl(String url) {this.url = url;}
    public String getUsername() {return username;}
    public void setUsername(String username) {this.username = username; }
    public String getPassword() { return password; }
    public void setPassword(String password) {this.password = password;}
    @Override
    public String toString() {
        return "DbConfiguration{" +
                "driverName='" + driverName + '\'' +
                ", url='" + url + '\'' +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

 在启动类上,通过@Import注解引入DbConfiguration,

package com.tjetc;
import com.tjetc.common.DbConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Import;
@SpringBootApplication
/*
springboot启动容器过程中,创建配置import的对象,纳入springboot容器的管理
*/
@Import(value = {DbConfiguration.class})
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

测试:

package com.tjetc;
import com.tjetc.common.DbConfiguration;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class ApplicationTests {
    @Autowired
    private DbConfiguration dbConfiguration;
    @Test
    public void testDbConfiguration() {
        System.out.println(dbConfiguration);
    }
}

结果:

DbConfiguration{driverName='com.mysql.cj.jdbc.Driver', url='jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai', username='root', password='123456'}

3.5多文件配置,加载顺序与位置

多文件配置:spring boot为了适应开发环境或生产环境的变化,专门打造profile通过指定参数来快速切换环境!

3.5.1.多文件的形式

格式: application-{profile}.properties 或 application-{profile}.yml

application-test.yml:

application-dev.yml:

application-prod.yml:

Spring boot提供几种的激活配置方式

3.5.2Spring boot提供几种的激活配置方式

一.JVM参数    -Dspring.profiles.active=prod

确定,启动springboot程序即可。

这时激活的是prod,端口号8083

二、配置文件(激活某个环境的配置)

在全局配置文件application.yml中激活相应的profile即可

此时,dev被激活,端口号为8082

三、命令行 : --spring.profiles.active=prod

先打包应用。

打开terminal,进入命令行,进入target目录:

运行jar包,激活profile

此时,prod被激活,端口号为8083

3.5.3加载顺序

SpringApplication将从以下位置加载application.yml文件, 并把它们添加到Spring环境上下文中:

优先级:当前目录下的/config子目录>当前目录>classpath下的/config包>classpath根路径

  1. 当前目录下的/config子目录
  2. 当前目录
  3. classpath下的/config包
  4. classpath根路径(项目root)

这个列表是按优先级排序的(列表中位置高的将覆盖位置低的),同时也可以使用properties文件替代YAML文件!

如果不希望使用默认的application.properties作为配置文件名,可以通过指定spring.config.name环境属性来切换其他的名称。 也可以使用spring.config.location环境属性来引用一个明确的路径(目录位置或文件路径列表以逗号分割)

java -jar myproject.jar --spring.config.location=c:/application.properties

Spring boot加载规则:优先级从高到低,高优先级的配置覆盖低优先级的配置,不同的配置混合一起使用。所有的配置由jar包外向jar内查找,优先加载带profile的,再加载不带profile的。

4SpringBoot整合MyBatis

4.1lombok

为了简化实体类的写法,可以使用lombok

4.1.1安装Lombok插件

4.1.2导入lombok依赖

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.20</version>
</dependency>

4.1.3使用lombok注解

package com.tjetc.entity;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
/*
@Data     lombok插件生成getter和setter方法
@Setter   lombok插件生成setter方法
@Getter   lombok插件生成getter方法
*/
@Data
public class User {
    private Long id;
    /*@Setter*/
    private String username;
    /*@Getter*/
    private String password;
}

在User上加上@Data注解,会自动拥有set方法,get方法,equals和hoshcode方法,toString方法。

4.2整合Mybatis注解开发

4.2.1导入依赖

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.0</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

4.2.2创建user表

4.2.3创建实体类

package com.tjetc.entity;
import lombok.Data;
@Data
public class User {
    private Long id;
    private String username;
    private String password;
}

4.2.4创建Mapper

package com.tjetc.dao;
import com.tjetc.entity.User;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface UserMapper {
    @Select("select id,username,`password` from `user`")
    List<User> selectAll();
}

4.2.5创建Service

package com.tjetc.service;
import com.tjetc.entity.User;
import java.util.List;
public interface UserService {
    List<User> findAll();
}

4.2.6创建service实现类

package com.tjetc.service.impl;
import com.tjetc.dao.UserMapper;
import com.tjetc.entity.User;
import com.tjetc.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;
    @Override
    public List<User> findAll() {
        return userMapper.selectAll();
    }
}

4.2.7编写controller

package com.tjetc.controller;
import com.tjetc.entity.User;
import com.tjetc.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/*@Controller*/
@RestController
/*
@RestController 相当于 @Controller+@ResponseBody
*/
@RequestMapping("user")
public class UserController {
    @Autowired
    private UserService userService;
    @RequestMapping("all")
    /*@ResponseBody//返回json*/
    public List<User> findAll() {
        return userService.findAll();
    }
}

4.2.8添加mapper扫描

package com.tjetc;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
/*
扫描指定包下,mapper接口生代理对象,交给springboot容器管理
*/
@MapperScan(basePackages = {"com.tjetc.dao"})
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

如果在UserMapper接口上添加@Mapper注解,则不需要添加mapper扫描。二者用一种就可以。

4.2.9添加配置

在application.properties中配置datasource

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123456

4.2.10测试

4.3基于xml整合mybatis

以查询所有用户为例。

4.3.1编写UserMapper

package com.tjetc.dao;
import com.tjetc.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/*@Mapper
mybatis会根据带有@Mapper注解的类生成代理对象,交给springboot容器管理
可以直接在Application中用 @MapperScan(basePackages = {"com.tjetc.dao"}) 替代
*/
public interface UserMapper {
    /*@Select("select id,username,`password` from `user`")*/
    List<User> selectAll();
}

4.3.2编写mapper映射

在resources下创建mapper目录,专门用来存放mybatis相关配置。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN "
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.tjetc.dao.UserMapper">
    <select id="selectAll" resultMap="userMap">
        select id,username,`password` from `user`
    </select>
    <resultMap id="userMap" type="user">
        <id column="id" property="id"></id>
        <result column="username" property="username"></result>
        <result column="password" property="password"></result>
    </resultMap>
</mapper>

4.3.3添加mybatis配置

在springboot的配置文件,接管mybatis核心配置:

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123456

#配置mybatis映射文件的位置
mybatis.mapper-locations=classpath:mapper/*Mapper.xml
#配置mybatis使用别名
mybatis.type-aliases-package=com.tjetc.entity

4.3.4启动测试

实现跟注解相同的效果。

4.4整合PageHelper

PageHelper是一款犀利的Mybatis分页插件,使用了这个插件之后,分页开发起来更加简单容易。

Spring Boot整合PageHelper不需要做任何配置文件的配置,添加依赖后就可以直接使用。

4.4.1导入依赖

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.4.3</version>
</dependency>

4.4.2准备数据

在service中实现分页,以查询所有用户为例:

4.4.3编写分页service

/**
 *
查询分页数据
 
* @param pageNum
 
* @param pageSize
 
* @return
 */
PageInfo<User> findPage(int pageNum, int pageSize);

@Override
public PageInfo<User> findPage(int pageNum, int pageSize) {
    PageHelper.startPage(pageNum,pageSize);
    //查询所有用户
    List<User> users = userMapper.selectAll();
    //获取Page
    PageInfo<User> userPageInfo = new PageInfo<>(users);
    return userPageInfo;
}   

4.4.4UserController

package com.tjetc.controller;
import com.github.pagehelper.PageInfo;
import com.tjetc.entity.User;
import com.tjetc.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/*@Controller*/
@RestController
/*
@RestController 相当于 @Controller+@ResponseBody
*/
@RequestMapping("user")
public class UserController {
    @Autowired
    private UserService userService;
    @RequestMapping("all")
    /*@ResponseBody//返回json*/
    public List<User> findAll() {
        return userService.findAll();
    }
    @RequestMapping("page")
    /*@ResponseBody//返回json*/
    public PageInfo<User> page(@RequestParam(value = "pageNum",required = false,defaultValue = "3") int pageNum,
                               @RequestParam(value = "pageSize",required = false,defaultValue = "3") int pageSize) {
        PageInfo<User> userPage = userService.findPage(pageNum, pageSize);
        return userPage;
    }
}

4.4.5结果

5SpringBoot整合logback

5.1日志框架设计思想

市面上存在的大量日志框架:

JUL、JCL、Jboss-logging、logback、log4j、log4j2、slf4j......

日志框架分为两类:

1、抽象类日志:

JCL、 slf4j、 Jboss-logging……

2、实现类日志

log4j、JUL、log4j2、logback……

Spring Boot使用的是SLF4j抽象和logback实现。

Logback是由log4j创始人设计的另一个开源日志组件,官方网站http://logback.qos.ch

它当前分为下面几个模块:

logback-core:其它两个模块的基础模块

logback-classic:它是log4j的一个改良版本,同时它完整实现了slf4j API使你可以很方便地更换成其它日志系统如log4j或JDK14 Logging

logback-access:访问模块与Servlet容器集成提供通过Http来访问日志的功能。

5.2 Spring Boot日志依赖原理和日志级别

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-logging</artifactId>
</dependency>

日志级别

在log4j2中, 共有8个级别,按照从低到高:

ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF。

  • All:最低等级的,用于打开所有日志记录.
  • Trace:是追踪,就是程序推进一下.
  • Debug:指出细粒度信息事件对调试应用程序是非常有帮助的.
  • Info:消息在粗粒度级别上突出强调应用程序的运行过程.
  • Warn:输出警告及warn以下级别的日志.
  • Error:输出错误信息日志.
  • Fatal:输出每个严重的错误事件将会导致应用程序的退出的日志.
  • OFF:最高等级的,用于关闭所有日志记录.

程序会打印高于或等于所设置级别的日志,设置的日志等级越高,打印出来的日志就越少 。

5.3Spring Boot日志默认配置

5.3.1日志级别配置

日志的默认级别是info,只有info级别,以及比info级别高的日志信息会打印。

Info,warn,error这三个级别的日志会打印。

在测试类中添加如下代码:

package com.tjetc;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class ApplicationTests {
    @Test
    public void testLog() {
        Logger logger = LoggerFactory.getLogger(ApplicationTests.class);
        logger.trace("trace跟踪日志");
        logger.debug("debug调试日志");
        logger.info("info输出日志");
        logger.warn("warn输出警告日志");
        logger.error("error错误或异常日志");
    }
}

2022-06-28 13:57:54.914  INFO 15024 --- [           main] com.tjetc.ApplicationTests               : info输出日志
2022-06-28 13:57:54.914  WARN 15024 --- [           main] com.tjetc.ApplicationTests               : warn输出警告日志
2022-06-28 13:57:54.914 ERROR 15024 --- [           main] com.tjetc.ApplicationTests               : error错误或异常日志

日志级别:logging.level.包名=级别

设置了日志的级别信息以后,大于等于该级别的日志信息都会打印,不是只打印当前级别,默认info级别

application.properties文件中可以修改日志的级别,修改com.tjetc下面的类在执行的时候的日志级别为debug。

logging.level.com.tjetc=debug

再次执行单元测试,debug级别的日志也会打印。

2022-06-28 14:00:44.403 DEBUG 11100 --- [           main] com.tjetc.ApplicationTests               : debug调试日志
2022-06-28 14:00:44.403  INFO 11100 --- [           main] com.tjetc.ApplicationTests               : info输出日志
2022-06-28 14:00:44.404  WARN 11100 --- [           main] com.tjetc.ApplicationTests               : warn输出警告日志
2022-06-28 14:00:44.404 ERROR 11100 --- [           main] com.tjetc.ApplicationTests               : error错误或异常日志

5.3.2日志文件与路径配置

Springboot的日志level来控制的,根据不同的level来显示。在哪里控制呢? Springboot默认的配置。

在application.properties中配置

logging.file.name= 文件完全名称

Springboot包下,logging包下的logback包下,有一个base.xml文件和defaults.xml文件。

Base.xml文件的内容:

 

Level:设置日志的级别为info。可以在控制台打印,也可以写入文件。

在全局配置文件application.properties中除了可以设置日志的级别:

#设置日志的级别
logging.level.com.tjetc=debug

 

还可以将日志写入文件中:

#将日志写入文件,在当前目录下生成一个logging.log文件
logging.file.name=logging.log

logging.file.name=C:/log/logging.log

上面的文件名可以是全路径名。比如C:/log/logging.log

如果不写盘符,直接写/log:在工程所在的盘符下创建一个log文件夹

应用一下:

@RequestMapping("abc")
public String abc(){
    try {
        int i = 1/0;
        return "执行成功";
    }catch (Exception e){
        //控制台打印
        e.printStackTrace();
        //文件中输出错误信息
        log.error(e.getMessage());
        return "执行失败或者出现异常";
    }
}

结果:

6SpringBoot整合JSP

6.1JSP简介

JSP全称Java Server Pages,是一种动态网页开发技术。它使用JSP标签在HTML网页中插入Java代码。标签通常以<%开头以%>结束。

JSP是一种Java servlet,主要用于实现Java web应用程序的用户界面部分。网页开发者们通过结合HTML代码、XHTML代码、XML元素以及嵌入JSP操作和命令来编写JSP。

JSP通过网页表单获取用户输入数据、访问数据库及其他数据源,然后动态地创建网页。

JSP标签有多种功能,比如访问数据库、记录用户选择信息、访问JavaBeans组件等,还可以在不同的网页中传递控制信息和共享信息。

6.2整合JSP

6.2.1创建工程

和之前建springboot项目一样

  • 直接next:
  • 填写相关信息:
  • 点击next
  • 导入web依赖,点击next,确定信息以后,finish。

6.2.2配置web.xml

在main下创建webapp/WEB-INF:

在IDEA窗口右上角点project constructor

添加web.xml

选择创建的WEB-INF目录,点击OK。最后在选中的WEB-INF路径后面添加web.xml,点击OK

Apply,OK。在WEB-INF中就有web.xml文件了。

6.2.3将webapp指定为web资源目录

webapp带蓝点,如下图,这样就可以了。

6.2.4添加jsp解析器依赖

<!--添加jsp解析器依赖-->
<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<!--  添加jstl -->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
</dependency>

6.2.5配置视图解析器

#配置查找页面的前缀
spring.mvc.view.prefix=/WEB-INF/views/
#配置查找页面的后缀
spring.mvc.view.suffix=.jsp

6.2.6编写controller

package com.tjetc.controller;
import com.github.pagehelper.PageInfo;
import com.tjetc.entity.User;
import com.tjetc.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
@Controller/*返回页面*/
/*@RestController
相当于 @Controller+@ResponseBody  返回json
*/
@RequestMapping("user")
public class UserController {
    @Autowired
    private UserService userService;
    @RequestMapping("all")
    /*@ResponseBody//返回json*/
    public String findAll(Model model) {
        List<User> users = userService.findAll();
        model.addAttribute("users", users);
        return "user-list";
    }
}

6.2.7创建jsp

在WEB-INF下创建views文件夹,在views下创建user-list.jsp文件。

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
    <head>
        <title>Title</title>
        <base href="<%=request.getContextPath()%>/">
    </head>
    <body>
        <table border="1" cellspacing="0" cellpadding="1">
            <tr>
                <td>编号</td>
                <td>用户名</td>
                <td>密码</td>
            </tr>
            <c:forEach items="${users}" var="user">
                <tr>
                    <td>${user.id}</td>
                    <td>${user.username}</td>
                    <td>${user.password}</td>
                </tr>
            </c:forEach>
        </table>
    </body>
</html>

代码编写完成,这时启动服务,会看到端口号8080,默认contextpath为””。访问controller的路径为localhost:8080/user/all,这时很有可能会404。解决办法:打开project constructor

idea右上角。

选择artifacts,选中springboot-jsp,右键,将该工程下所有的jar包导到左边。点击put into output root即可。会看到右边所有的jar都到了lib下。

重启服务,如果这时是404的话。解决方法:

idea右上角,edit configurations

选择SpringBOotJSPApplication,点开Environment,选择Workingdirectory,在列表中选择ModuleFileDir,确定即可。重新启动服务。访问controller。会看到如下内容:

7SpringBoot异常处理

7.1异常显示的页面

7.1.1 spring-boot-starter-thymeleaf 

Thymeleaf的使用非常简单,只需要把我们的html页面放在类路径下的templates下,thymeleaf就可以帮我们自动渲染了。

添加依赖()

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

加入依赖后,application.properties中有默认前缀和后缀

默认情况下,SpringBoot 项目错误页面如下:

当项目实际上线,如果给用户显示这个页面就不是很友好。当系统出现异常时应该给用户更加友好的错误页面

下面我们来看具体是如何实现的。

  1. templates/下新建error文件夹,在error中新建:状态.html的页面。例如当出现500时显示的页面为html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>500</title>
    </head>
    <body>
        <h1>您的代码出现了内部错误,请检查!</h1>
    </body>
</html>
  1. 创建controller
package com.tjetc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloController {
    @RequestMapping("hello")
    public String hello() {
        System.out.println(5 / 0);
        return "hello";
    }
}

我们知道,在运行上面代码的时候发生算术异常。错误码为500。

  1. 使用X进行模糊匹配
    1. 当出现5开头状态码的错误时,显示页面可以命名为html
    2. 如果html和5xx.html同时存在,则会精确匹配相应页面。

我们把刚才的500.html改为5xx.html,也是可以的。因为发生了5开头的异常,会走5xx.html。

执行结果:

如果500.html和5xx.html同时存在的话,可以会根据状态码精确匹配,如果没有相关状态码的html,则还是执行5xx.html

500.html和5xx.html同时存在,则会根据状态码精确匹配500.html。

  1. 统一错误页面显示

在templates下新建error.html。如果项目中不存在具体状态码的页面或没有使用x成功匹配的页面时,显示error.html作为错误显示页面。

比如我们在error下没有设置4xx相关的错误页面,当发生状态码为4xx的错误时,找不到相关的错误处理页面,这时会走一个统一的错误处理页面。一般会在templates下创建error.html,作为统一的错误处理页面。

在templates下创建error.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>error</title>
    </head>
    <body>
        error
    </body>
</html>

当发生404错误时,会自动走error.html

7.1.2 自定义异常显示的页面

package com.tjetc.controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class AdviceController {
    @ExceptionHandler({Exception.class})
    public String error() {
        return "bbb/error.html";
    }
}

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>error</title>
    </head>
    <body>
        bbb/error
    </body>
</html>

7.2异常处理

在Spring Boot项目中除了设置错误页面,还可以通过注解实现错误处理。

7.2.1局部异常

在Spring Boot项目中除了设置错误页面,还可以通过注解实现错误处理。

局部异常:

在控制器类中添加一个方法,结合@ExceptionHandler。但是只能对当前控制器中方法出现异常进行解决。

  1. 创建异常信息类
package com.tjetc.exception;
import lombok.Data;
@Data
public class ExceptionMessage {
    private String code;
    private String msg;
}
  1. 在controller中设置异常处理
package com.tjetc.controller;
import com.tjetc.exception.ExceptionMessage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
/*@Controller*/
@RestController
public class ErrorController {
    @RequestMapping("hello")
    public String hello() {
        System.out.println(5 / 0);
        return "hello";
    }
    /*局部异常处理*/
    @ExceptionHandler(ArithmeticException.class)
    public ExceptionMessage exception(Exception e) {
        ExceptionMessage message = new ExceptionMessage();
        message.setCode("500");
        message.setMsg("出错了:" + e.getMessage());
        return message;
    }
}

@ExceptionHandler的参数为发生异常的类型。如果controller的方法中捕获到了这种异常,就会走@ExceptionHandler表示的方法arithmeticException(),在方法参数中,可以获取异常对象。

最终执行结果:

当访问test的controller方法时,会出现除0异常,就会走异常处理方法,封装异常信息,返回。

7.2.2全局异常

新建全局异常类,通过@ControllerAdvice结合@ExceptionHandler。当全局异常处理和局部处理同时存在时,局部生效(就近原则)

  1. 编写异常处理controller
package com.tjetc.controller;
import com.tjetc.exception.ExceptionMessage;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
/*
全局异常处理的Controller
*/
@ControllerAdvice
public class GlobalExceptionHandleController {
    //发生除0异常时,会执行该方法
    @ExceptionHandler(ArithmeticException.class)
    @ResponseBody
    public ExceptionMessage arithmeticException(Exception e) {
        System.out.println("GlobalExceptionHandleController的全局异常处理");
        ExceptionMessage message = new ExceptionMessage();
        message.setCode("500");
        message.setMsg("除0异常:" + e.getMessage());
        return message;
    }
    //发生空指针异常时,会执行该方法
    @ExceptionHandler(NullPointerException.class)
    @ResponseBody
    public ExceptionMessage nullPointerException(Exception e) {
        ExceptionMessage message = new ExceptionMessage();
        message.setCode("500");
        message.setMsg("空指针异常:" + e.getMessage());
        return message;
    }
    //发生其他异常时,会执行该方法
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public ExceptionMessage exception(Exception e) {
        ExceptionMessage message = new ExceptionMessage();
        message.setCode("500");
        message.setMsg("非数字或空指针异常,未知异常");
        return message;
    }
}
  1. 编写controller(先局部处理,局部没有去全局)
package com.tjetc.controller;
import com.tjetc.exception.ExceptionMessage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
/*@Controller*/
@RestController
public class ErrorController {
    @RequestMapping("hello")
    public String hello() {
        System.out.println(5 / 0);
        return "hello";
    }
    @RequestMapping("aaa")
    public String aaa() {
        String str = null;
        System.out.println(str.length());
        return "aaa";
    }
    @RequestMapping("bbb")
    public String bbb() {
        int[] i = new int[3];
        int num = i[3];//数组越界
        return "bbb";
    }
    /*局部异常处理*/
    @ExceptionHandler(ArithmeticException.class)
    public ExceptionMessage exception(Exception e) {
        System.out.println("ErrorController的局部异常处理");
        ExceptionMessage message = new ExceptionMessage();
        message.setCode("500");
        message.setMsg("出错了:" + e.getMessage());
        return message;
    }
}
  1. 测试结果

(1)

 

(2)

(3)

8SpringBoot定时任务

8.1 Scheduled简介

Scheduled是Spring3.0后内置的定时任务器。通过Scheduled可以完成周期的执行一些功能。存在于spring-conext-support.jar中。

在SpringBoot中使用Scheduled非常简单,只需要在对应的方法上添加@Scheduled注解,再配置对应的参数就可以完成。

8.1.1创建工程

next,finish。

8.1.2添加依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
</dependency>

8.1.3在启动类上添加注解

package com.tjetc;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling//启动调度
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

在启动类上添加@EnableScheduling注解

8.1.4创建定时任务

package com.tjetc.scheduled;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class ScheduledDemo {
    //调度注解 cron填写表达式 每个参数之间有空格
    @Scheduled(cron = "0/2 * * * * *")
    public void test(){
        //模拟业务逻辑
        System.out.println("test scheduled");
    }
}

8.1.5启动服务

启动spring服务,运行结果如下,每隔两秒执行一次任务。

8.2Cron表达式

在线Cron表达式生成器:https://cron.qqe2.com/

Cron表达式是一个字符串,分为6或7个域,每一个域代表一个含义

{秒数} {分钟} {小时} {日期} {月份} {星期} {年份(可为空)}

Cron有如下两种语法格式:

  • Seconds Minutes Hours Day Month Week Year
  • Seconds Minutes Hours Day Month Week 

corn从左到右(用空格隔开):

秒 分 小时 月份中的日期 月份 星期中的日期 年份

位置

时间域名

允许值

允许的特殊字符

1

0-59

, - * /

2

分钟

0-59

, - * /

3

小时

0-23

, - * /

4

1-31

, - * / L W C

5

1-12

, - * /

6

星期

1-7(周日到周六)

, - * ? / L C #

7

年(可选)

1970-2099

, - * /

Cron表达式的时间字段除允许设置数值外,还可使用一些特殊的字符,提供列表、范围、通配符等功能,细说如下:

  • 星号(*):可用在所有字段中,表示对应时间域的每一个时刻,例如,*在分钟字段时,表示“每分钟”;
  • 问号(?) :该字符只在日期和星期字段中使用,它通常指定为“无意义的值”,相当于占位符
  • 减号(-):表达一个范围,如在小时字段中使用“10-12”,则表示从10到12点,即10,11,12;
  • 逗号(,):表达一个列表值,如在星期字段中使用“MON,WED,FRI”,则表示星期一,星期三和星期五;
  • 斜杠(/):x/y表达一个等步长序列,x为起始值,y为增量步长值。如在秒字段中使用0/15,则表示为0,15,30和45秒,而5/15在分钟字段中表示5,20,35,50,你也可以使用*/y,它等同于0/y;
  • L:该字符只在日期和星期字段中使用,代表“Last”的意思,但它在两个字段中意思不同。L在日期字段中,表示这个月份的最后一天,如一月的31号,非闰年二月的28号;如果L用在星期中,则表示星期六,等同于7。但是,如果L出现在星期字段里,而且在前面有一个数值X,则表示“这个月的最后X天”,例如,6L表示该月的最后星期五;
  • W:该字符只能出现在日期字段里,是对前导日期的修饰,表示离该日期最近的工作日。例如15W表示离该月15号最近的工作日,如果该月15号是星期六,则匹配14号星期五;如果15日是星期日,则匹配16号星期一;如果15号是星期二,那结果就是15号星期二。但必须注意关联的匹配日期不能够跨月,如你指定1W,如果1号是星期六,结果匹配的是3号星期一,而非上个月最后的那天。W字符串只能指定单一日期,而不能指定日期范围
  • LW组合:在日期字段可以组合使用LW,它的意思是当月的最后一个工作日;
  • 井号(#):该字符只能在星期字段中使用,表示当月某个工作日。如6#3表示当月的第三个星期五(6表示星期五,#3表示当前的第三个),而4#5表示当月的第五个星期三,假设当月没有第五个星期三,忽略不触发;
  • C:该字符只在日期和星期字段中使用,代表“Calendar”的意思。它的意思是计划所关联的日期,如果日期没有被关联,则相当于日历中所有日期。例如5C在日期字段中就相当于日历5日以后的第一天。1C在星期字段中相当于星期日后的第一天。

Cron表达式对特殊字符的大小写不敏感,对代表星期的缩写英文大小写也不敏感。

    例子:

  • @Scheduled(cron = "0 0 1 1 1 ?")//每年一月的一号的1:00:00 执行一次
  • @Scheduled(cron = "0 0 1  1 1,6 ?") //一月和六月的一号的1:00:00 执行一次
  • @Scheduled(cron = "0 0 1  1 1,4,7,10 ?") //每个季度的第一个月的一号的1:00:00 执行一次
  • @Scheduled(cron = “0 0 0 11 11 ?”) 11月11日0点执行

 

Cron表达式对特殊字符的大小写不敏感,对代表星期的缩写英文大小写也不敏感。

9SpringBoot与web开发

9.1静态资源映射规则

“/**” 访问当前项目任何资源,全部找静态资源的文件夹进行映射

静态资源的文件夹:

SpringBoot支持的目录映射包含(优先级从上到下,即若有相同文件资源时会先执行:

META-INF/resources/ > resource > static > public > / ,注意:自己指定静态资源路径时以上目录不起作用!):

  • classpath:/META-INF/resources/
  • classpath:/resources/
  • classpath:/static/
  • classpath:/public/
  • /:当前项目的根路径

静态资源路径下的文件,可以通过地址栏直接访问

可以直接在地址栏中访问:


3.“/**” 访问静态资源文件夹下的所有index.html页面

在resources下创建index.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <h1>index</h1>
    </body>
</html>

通过地址栏直接访问index.html

如果aaa.html的位置在/static/aaa/aaa.html,则相应的访问路径也要为/aaa/aaa.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <h1>aaa</h1>
    </body>
</html>

9.2自定义静态资源映射规则

自定义配置类

package com.tjetc.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class MyWebAppConfigurer implements WebMvcConfigurer {
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**").
                addResourceLocations(
                        "classpath:/META-INF/resources/",
                        "classpath:/resources/",
                        "classpath:/static/",
                        "classpath:/public/",
                        "file:C:/image/img/");
    }
}

完善上面的操作,动态处理路径

image.base.path=C:/image/img/

package com.tjetc.common;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
@Component
@PropertySource("classpath:user.properties")
public class CommonConstants {
    @Value("${image.base.path}")
    public String IMAGE_BASE_PATH;
}

package com.tjetc.config;
import com.tjetc.common.CommonConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/*
资源映射路径
 */
@Configuration
public class MyWebAppConfigurer implements WebMvcConfigurer {
    @Autowired
    private CommonConstants constants;
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        /*
        addResourceHandler 添加资源处理url路径
        addResourceLocations 添加url对应的磁盘物理路径
        */
        registry.addResourceHandler("/**").
                addResourceLocations(
                        "classpath:/META-INF/resources/",
                        "classpath:/resources/",
                        "classpath:/static/",
                        "classpath:/public/",
                        /*"file:C:/image/img/");//添加自定义资源映射路径*/
                        "file:" + constants.IMAGE_BASE_PATH);/*动态处理*/
    }
}

9.3登录拦截器

9.3.1编写controller

@Controller
@RequestMapping("user")
public class UserController {
    /**
     *
登录验证
    
*
     * @param
username
    
* @param password
    
* @param session
    
* @param model
    
* @return
     */
   
@RequestMapping("login")
    public String login(@RequestParam("username") String username,
                        @RequestParam("password") String password,
                        HttpSession session, Model model) {
        //模拟数据库查询
       
if (username.equals("zhangsan") && password.equals("111")) {
            session.setAttribute("username", "zhangsan");
            return "redirect:/user/welcome";
        } else {
            model.addAttribute("msg", "用户名或者密码错误");
            return "login";
        }
    }
    /**
     *
登录的页面
    
*
     * @param
model
    
* @return
     */
   
@RequestMapping("login-html")
    public String loginHtml(Model model) {
        model.addAttribute("msg", "");
        return "login";
    }
    /**
     *
欢迎首页
    
*
     * @return
     */
   
@RequestMapping("welcome")
    public String welcome() {
        return "welcome";
    }
}

 

 

登录页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/user/login" method="post">
        用户名:<input type="text" name="username"><br>
        密码:<input type="password" name="password"><br>
        <input type="submit" value="登录"><br>
        <span th:if="${msg}!=''" th:text="${msg}"></span>
    </form>
</body>
</html>

 

 

9.3.2编写登录拦截器

public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //获取session
       
HttpSession session = request.getSession();
        //判断session总是否存在用户名
       
String username = (String) session.getAttribute("username");
        if (username == null) {//没有登录或者登录过期跳转登录页面
           
request.setAttribute("msg", "");
            //重定向到请求/user/login-html
           
response.sendRedirect("/user/login-html");
            //返回false,不执行后面的拦截器和Controller对应的方法
           
return false;
        }
        //放行
       
return true;
    }
}

 

9.3.3配置拦截器

@Configuration
public class LoginInterceptorConfig implements WebMvcConfigurer {
    /**
     *
注册登录拦截器
    
*
     * @param
registry
    
*/
   
@Override
    public void addInterceptors(InterceptorRegistry registry) {
        //注册拦截器
       
registry.addInterceptor(new LoginInterceptor())
                //拦截器要拦截url
               
.addPathPatterns("/**")
                //排除拦截器要拦截的url
               
.excludePathPatterns("/user/login-html", "/user/login", "/css/**", "/js/**", "/img/**");
    }
}

 

 

 

9.4登录拦截器(JSON的应用)

9.4.1添加依赖,编写JsonResult

<!--json字符串与对象相互转换-->
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.74</version>
</dependency>

package com.tjetc.model;
public class JsonResult<T> {
    //0 代表成功,1代表失败,-1代表登录过期
    private int state;
    //失败的提示信息
    private String message;
    //返回的数据
    private T data;
    public JsonResult(int state, String message, T data) {
        this.state = state;
        this.message = message;
        this.data = data;
    }
    public int getState() {return state;}
    public String getMessage() {return message;}
    public T getData() {return data;}
}

9.4.2配置拦截器

package com.tjetc.config;
import com.tjetc.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class LoginWebMvcConfigurer implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor()).
                //拦截器要拦截url
                        addPathPatterns("/**").
                //排除拦截器要拦截的url
                        excludePathPatterns("/login", "/css/**",
                        "/js/**", "/img/**");
    }
}

9.4.3编写controller

package com.tjetc.controller;
import com.tjetc.model.JsonResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpSession;
@RestController
public class LoginController {
    @RequestMapping("login")
    public JsonResult login(@RequestParam("username")String username,
                            @RequestParam("password")String password,
                            HttpSession session){
        //数据库判断 todo
        if("carat".equals(username)&&"17526".equals(password)){
            session.setAttribute("username",username);
            JsonResult<Object> jsonResult = new JsonResult<>(0, "登录成功", null);
            return jsonResult;
        }else{
            JsonResult<Object> jsonResult = new JsonResult<>(1, "用户密码错误", null);
            return jsonResult;
        }
    }
}

9.4.4编写登录拦截器

package com.tjetc.interceptor;
import com.alibaba.fastjson.JSONObject;
import com.tjetc.model.JsonResult;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession();
        Object username = session.getAttribute("username");
        if (username == null) {
            response.setContentType("text/html;charset=utf-8");
            JsonResult<Object> jsonResult = new JsonResult<>(-1, "未登录或登录过期", null);
            String json = JSONObject.toJSONString(jsonResult);
            response.getWriter().write(json);
            return false;/*不放行*/
        } else {
            return true;
        }
    }
}

登陆成功前 

登陆成功后

posted @ 2022-07-12 08:58  carat9588  阅读(111)  评论(0编辑  收藏  举报