SpringBoot教案

SpringBoot基础

学习目标:

  1. 能够理解Spring的优缺点

  2. 能够理解SpringBoot的特点

  3. 能够理解SpringBoot的核心功能

  4. 能够搭建SpringBoot的环境

  5. 能够完成application.properties配置文件的配置

  6. 能够完成application.yml配置文件的配置

  7. 能够使用SpringBoot集成Mybatis

  8. 能够使用SpringBoot集成Junit

  9. 能够使用SpringBoot集成SpringData JPA

  10. 能够使用springBoot集成redis,并操作redis

  11. 能够使用springBoot集成thymeleaf和freemarker

  12. 能够使用springBoot集成swagger

  13. 能够使用springBoot的定时任务

一、SpringBoot简介

1.1 原有Spring优缺点分析

1.1.1 Spring的优点分析

Spring是Java企业版(Java Enterprise Edition,JEE,也称J2EE)的轻量级代替品。无需开发重量级的Enterprise JavaBean(EJB),Spring为企业级Java开发提供了一种相对简单的方法,通过依赖注入和面向切面编程,用简单的Java对象(Plain Old Java Object,POJO)实现了EJB的功能。

1.1.2 Spring的缺点分析

虽然Spring的组件代码是轻量级的,但它的配置却是重量级的。一开始,Spring用XML配置,而且是很多XML配置。Spring 2.5引入了基于注解的组件扫描,这消除了大量针对应用程序自身组件的显式XML配置。Spring 3.0引入了基于Java的配置,这是一种类型安全的可重构配置方式,可以代替XML。

所有这些配置都代表了开发时的损耗。因为在思考Spring特性配置和解决业务问题之间需要进行思维切换,所以编写配置挤占了编写应用程序逻辑的时间。和所有框架一样,Spring实用,但与此同时它要求的回报也不少。

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

1.2 SpringBoot的概述

1.2.1 SpringBoot解决上述Spring的缺点

SpringBoot对上述Spring的缺点进行的改善和优化,基于约定优于配置的思想,可以让开发人员不必在配置与逻辑业务之间进行思维的切换,全身心的投入到逻辑业务的代码编写中,从而大大提高了开发的效率,一定程度上缩短了项目周期。

1.2.2 SpringBoot的特点

  • 为基于Spring的开发提供更快的入门体验

  • 开箱即用,没有代码生成,也无需XML配置。同时也可以修改默认值来满足特定的需求

  • 提供了一些大型项目中常见的非功能性特性,如嵌入式服务器、安全、指标,健康检测、外部配置等

  • SpringBoot不是对Spring功能上的增强,而是提供了一种快速使用Spring的方式

1.2.3 SpringBoot的核心功能

  • 起步依赖

    起步依赖本质上是一个Maven项目对象模型(Project Object Model,POM),定义了对其他库的传递依赖,这些东西加在一起即支持某项功能。

    简单的说,起步依赖就是将具备某种功能的坐标打包到一起,并提供一些默认的功能。

  • 自动配置

    Spring Boot的自动配置是一个运行时(更准确地说,是应用程序启动时)的过程,考虑了众多因素,才决定Spring配置应该用哪个,不该用哪个。该过程是Spring自动完成的。

 

注意:起步依赖和自动配置的原理剖析会在第三章《SpringBoot原理分析》进行详细讲解

二、SpringBoot快速入门

2.1 代码实现

2.1.1 创建Maven工程

使用idea工具创建一个maven工程,该工程为普通的java工程即可

 

 

 

 

 

 

 

 

2.1.2 添加SpringBoot的起步依赖

SpringBoot要求,项目要继承SpringBoot的起步依赖spring-boot-starter-parent

<parent>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-parent</artifactId>
   <version>2.0.1.RELEASE</version>
</parent>

SpringBoot要集成SpringMVC进行Controller的开发,所以项目要导入web的启动依赖

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

 

2.1.3 编写SpringBoot引导类

要通过SpringBoot提供的引导类起步SpringBoot才可以进行访问

package com.itheima;
​
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
​
@SpringBootApplication
public class MySpringBootApplication {
​
   public static void main(String[] args) {
       SpringApplication.run(MySpringBootApplication.class);
  }
​
}

2.1.4 编写Controller

在引导类MySpringBootApplication同级包或者子级包中创建QuickStartController

package com.itheima.controller;
​
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
​
@Controller
public class QuickStartController {
   
   @RequestMapping("/quick")
   @ResponseBody
   public String quick(){
       return "springboot 访问成功!";
  }
   
}

2.1.5 测试

执行SpringBoot起步类的主方法,控制台打印日志如下:

.   ____          _            __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot ::       (v2.0.1.RELEASE)
​
2018-05-08 14:29:59.714 INFO 5672 --- [           main] com.itheima.MySpringBootApplication     : Starting MySpringBootApplication on DESKTOP-RRUNFUH with PID 5672 (C:\Users\muzimoo\IdeaProjects\IdeaTest\springboot_quick\target\classes started by muzimoo in C:\Users\muzimoo\IdeaProjects\IdeaTest)
... ... ...
o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-05-08 14:30:03.126 INFO 5672 --- [           main] o.s.j.e.a.AnnotationMBeanExporter       : Registering beans for JMX exposure on startup
2018-05-08 14:30:03.196 INFO 5672 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2018-05-08 14:30:03.206 INFO 5672 --- [           main] com.itheima.MySpringBootApplication     : Started MySpringBootApplication in 4.252 seconds (JVM running for 5.583)

通过日志发现,Tomcat started on port(s): 8080 (http) with context path ''

tomcat已经起步,端口监听8080,web应用的虚拟工程名称为空

打开浏览器访问url地址为:http://localhost:8080/quick

 

2.2 快速入门解析

2.2.2 SpringBoot代码解析

  • @SpringBootApplication:标注SpringBoot的启动类,该注解具备多种功能(后面详细剖析)

  • SpringApplication.run(MySpringBootApplication.class) 代表运行SpringBoot的启动类,参数为SpringBoot启动类的字节码对象

2.2.3 SpringBoot工程热部署

我们在开发中反复修改类、页面等资源,每次修改后都是需要重新启动才生效,这样每次启动都很麻烦,浪费了大量的时间,我们可以在修改代码后不重启就能生效,在 pom.xml 中添加如下配置就可以实现这样的功能,我们称之为热部署。

<!--热部署配置
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-devtools</artifactId>
</dependency>-->

注意:IDEA进行SpringBoot热部署失败原因

出现这种情况,并不是热部署配置问题,其根本原因是因为Intellij IEDA默认情况下不会自动编译,需要对IDEA进行自动编译的设置,如下:

 

然后 Shift+Ctrl+Alt+/,选择Registry

 

2.2.4 使用idea快速创建SpringBoot项目

 

通过idea快速创建的SpringBoot项目的pom.xml中已经导入了我们选择的web的起步依赖的坐标

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
​
<groupId>com.itheima</groupId>
<artifactId>springboot_quick2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
​
<name>springboot_quick2</name>
<description>Demo project for Spring Boot</description>
​
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
​
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>9</java.version>
</properties>
​
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
​
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
​
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
​
​
</project>
​

可以使用快速入门的方式创建Controller进行访问,此处不再赘述

 

三、SpringBoot原理分析

3.1 起步依赖原理分析

3.1.1 分析spring-boot-starter-parent

按住Ctrl点击pom.xml中的spring-boot-starter-parent,跳转到了spring-boot-starter-parent的pom.xml,xml配置如下(只摘抄了部分重点配置):

<parent>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-dependencies</artifactId>
 <version>2.0.1.RELEASE</version>
 <relativePath>../../spring-boot-dependencies</relativePath>
</parent>

按住Ctrl点击pom.xml中的spring-boot-starter-dependencies,跳转到了spring-boot-starter-dependencies的pom.xml,xml配置如下(只摘抄了部分重点配置):

<properties>
<activemq.version>5.15.3</activemq.version>
<antlr2.version>2.7.7</antlr2.version>
<appengine-sdk.version>1.9.63</appengine-sdk.version>
<artemis.version>2.4.0</artemis.version>
<aspectj.version>1.8.13</aspectj.version>
<assertj.version>3.9.1</assertj.version>
<atomikos.version>4.0.6</atomikos.version>
<bitronix.version>2.1.4</bitronix.version>
<build-helper-maven-plugin.version>3.0.0</build-helper-maven-plugin.version>
<byte-buddy.version>1.7.11</byte-buddy.version>
... ... ...
</properties>
<dependencyManagement>
<dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot</artifactId>
      <version>2.0.1.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-test</artifactId>
      <version>2.0.1.RELEASE</version>
    </dependency>
    ... ... ...
</dependencies>
</dependencyManagement>
<build>
<pluginManagement>
  <plugins>
    <plugin>
      <groupId>org.jetbrains.kotlin</groupId>
      <artifactId>kotlin-maven-plugin</artifactId>
      <version>${kotlin.version}</version>
    </plugin>
    <plugin>
      <groupId>org.jooq</groupId>
      <artifactId>jooq-codegen-maven</artifactId>
      <version>${jooq.version}</version>
    </plugin>
    <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
      <version>2.0.1.RELEASE</version>
    </plugin>
        ... ... ...
  </plugins>
</pluginManagement>
</build>

从上面的spring-boot-starter-dependencies的pom.xml中我们可以发现,一部分坐标的版本、依赖管理、插件管理已经定义好,所以我们的SpringBoot工程继承spring-boot-starter-parent后已经具备版本锁定等配置了。所以起步依赖的作用就是进行依赖的传递。

3.1.2 分析spring-boot-starter-web

按住Ctrl点击pom.xml中的spring-boot-starter-web,跳转到了spring-boot-starter-web的pom.xml,xml配置如下(只摘抄了部分重点配置):

<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starters</artifactId>
  <version>2.0.1.RELEASE</version>
</parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.0.1.RELEASE</version>
<name>Spring Boot Web Starter</name>
 
<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <version>2.0.1.RELEASE</version>
    <scope>compile</scope>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-json</artifactId>
    <version>2.0.1.RELEASE</version>
    <scope>compile</scope>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    <version>2.0.1.RELEASE</version>
    <scope>compile</scope>
  </dependency>
  <dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.0.9.Final</version>
    <scope>compile</scope>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>5.0.5.RELEASE</version>
    <scope>compile</scope>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.0.5.RELEASE</version>
    <scope>compile</scope>
  </dependency>
</dependencies>
</project>
​

从上面的spring-boot-starter-web的pom.xml中我们可以发现,spring-boot-starter-web就是将web开发要使用的spring-web、spring-webmvc等坐标进行了“打包”,这样我们的工程只要引入spring-boot-starter-web起步依赖的坐标就可以进行web开发了,同样体现了依赖传递的作用。

 

3.2 自动配置原理解析

按住Ctrl点击查看启动类MySpringBootApplication上的注解@SpringBootApplication

@SpringBootApplication
public class MySpringBootApplication {
   public static void main(String[] args) {
       SpringApplication.run(MySpringBootApplication.class);
  }
}

注解@SpringBootApplication的源码

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
​
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {};
​
... ... ...
​
}

其中,

@SpringBootConfiguration:等同与@Configuration,既标注该类是Spring的一个配置类

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

按住Ctrl点击查看注解@EnableAutoConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
... ... ...
}

其中,@Import(AutoConfigurationImportSelector.class) 导入了AutoConfigurationImportSelector类

按住Ctrl点击查看AutoConfigurationImportSelector源码

public String[] selectImports(AnnotationMetadata annotationMetadata) {
      ... ... ...
       List<String> configurations = getCandidateConfigurations(annotationMetadata,
                                                                  attributes);
       configurations = removeDuplicates(configurations);
       Set<String> exclusions = getExclusions(annotationMetadata, attributes);
       checkExcludedClasses(configurations, exclusions);
       configurations.removeAll(exclusions);
       configurations = filter(configurations, autoConfigurationMetadata);
       fireAutoConfigurationImportEvents(configurations, exclusions);
       return StringUtils.toStringArray(configurations);
}
​
​
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());

return configurations;
}
​

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

 

spring.factories 文件中有关自动配置的配置信息如下:

... ... ...
​
org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
​
... ... ...

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

我们以ServletWebServerFactoryAutoConfiguration为例来分析源码:

@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
... ... ...
}
​

其中,

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

进入ServerProperties.class源码如下:

@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
​
/**
* Server HTTP port.
*/
private Integer port;
​
/**
* Network address to which the server should bind.
*/
private InetAddress address;
 
... ... ...
 
}

其中,

prefix = "server" 表示SpringBoot配置文件中的前缀,SpringBoot会将配置文件中以server开始的属性映射到该类的字段中。映射关系如下:

 

四、SpringBoot的配置文件

4.1 SpringBoot配置文件类型

4.1.1 SpringBoot配置文件类型和作用

SpringBoot是基于约定的,所以很多配置都有默认值,但如果想使用自己的配置替换默认配置的话,就可以使用application.properties或者application.yml(application.yaml)进行配置。

SpringBoot默认会从Resources目录下加载application.properties或application.yml(application.yaml)文件

其中,application.properties文件是键值对类型的文件,之前一直在使用,所以此处不在对properties文件的格式进行阐述。除了properties文件外,SpringBoot还可以使用yml文件进行配置,下面对yml文件进行讲解。

4.1.2 application.yml配置文件

4.1.2.1 yml配置文件简介

YML文件格式是YAML (YAML Aint Markup Language)编写的文件格式,YAML是一种直观的能够被电脑识别的的数据数据序列化格式,并且容易被人类阅读,容易和脚本语言交互的,可以被支持YAML库的不同的编程语言程序导入,比如: C/C++, Ruby, Python, Java, Perl, C#, PHP等。YML文件是以数据为核心的,比传统的xml方式更加简洁。

YML文件的扩展名可以使用.yml或者.yaml。

4.1.2.2 yml配置文件的语法

4.1.2.2.1 配置普通数据
  • 语法: key: value

  • 示例代码:

  • name: haohao
  • 注意:value之前有一个空格

4.1.2.2.2 配置对象数据
  • 语法:

    key:

    key1: value1

    key2: value2

    或者:

    key: {key1: value1,key2: value2}

  • 示例代码:

  • person:
    name: haohao
    age: 31
    addr: beijing
    ​
    #或者
    ​
    person: {name: haohao,age: 31,addr: beijing}
  • 注意:key1前面的空格个数不限定,在yml语法中,相同缩进代表同一个级别

4.1.2.2.2 配置Map数据

同上面的对象写法

4.1.2.2.3 配置数组(List、Set)数据
  • 语法:

    key:

    - value1

    - value2

    或者:

    key: [value1,value2]

  • 示例代码:

  • city:
    - beijing
    - tianjin
    - shanghai
    - chongqing
     
    #或者
    ​
    city: [beijing,tianjin,shanghai,chongqing]
    ​
    #集合中的元素是对象形式
    student:
    - name: zhangsan
      age: 18
      score: 100
    - name: lisi
      age: 28
      score: 88
    - name: wangwu
      age: 38
      score: 90
  • 注意:value1与之间的 - 之间存在一个空格

4.1.3 SpringBoot配置信息的查询

上面提及过,SpringBoot的配置文件,主要的目的就是对配置信息进行修改的,但在配置时的key从哪里去查询呢?我们可以查阅SpringBoot的官方文档

文档URL:https://docs.spring.io/spring-boot/docs/2.0.1.RELEASE/reference/htmlsingle/#common-application-properties

常用的配置摘抄如下:

# QUARTZ SCHEDULER (QuartzProperties)
spring.quartz.jdbc.initialize-schema=embedded # Database schema initialization mode.
spring.quartz.jdbc.schema=classpath:org/quartz/impl/jdbcjobstore/tables_@@platform@@.sql # Path to the SQL file to use to initialize the database schema.
spring.quartz.job-store-type=memory # Quartz job store type.
spring.quartz.properties.*= # Additional Quartz Scheduler properties.
​
# ----------------------------------------
# WEB PROPERTIES
# ----------------------------------------
​
# EMBEDDED SERVER CONFIGURATION (ServerProperties)
server.port=8080 # Server HTTP port.
server.servlet.context-path= # Context path of the application.
server.servlet.path=/ # Path of the main dispatcher servlet.
​
# HTTP encoding (HttpEncodingProperties)
spring.http.encoding.charset=UTF-8 # Charset of HTTP requests and responses. Added to the "Content-Type" header if not set explicitly.
​
# JACKSON (JacksonProperties)
spring.jackson.date-format= # Date format string or a fully-qualified date format class name. For instance, `yyyy-MM-dd HH:mm:ss`.
​
# SPRING MVC (WebMvcProperties)
spring.mvc.servlet.load-on-startup=-1 # Load on startup priority of the dispatcher servlet.
spring.mvc.static-path-pattern=/** # Path pattern used for static resources.
spring.mvc.view.prefix= # Spring MVC view prefix.
spring.mvc.view.suffix= # Spring MVC view suffix.
​
# DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
spring.datasource.driver-class-name= # Fully qualified name of the JDBC driver. Auto-detected based on the URL by default.
spring.datasource.password= # Login password of the database.
spring.datasource.url= # JDBC URL of the database.
spring.datasource.username= # Login username of the database.
​
# JEST (Elasticsearch HTTP client) (JestProperties)
spring.elasticsearch.jest.password= # Login password.
spring.elasticsearch.jest.proxy.host= # Proxy host the HTTP client should use.
spring.elasticsearch.jest.proxy.port= # Proxy port the HTTP client should use.
spring.elasticsearch.jest.read-timeout=3s # Read timeout.
spring.elasticsearch.jest.username= # Login username.
​

我们可以通过配置application.poperties 或者 application.yml 来修改SpringBoot的默认配置

例如:

application.properties文件

server.port=8888
server.servlet.context-path=demo

application.yml文件

server:
  port: 8888
  servlet:
    context-path: /demo

 

4.2 配置文件与配置类的属性映射方式

4.2.1 使用注解@Value映射

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

例如:

application.properties配置如下:

person:
  name: zhangsan
  age: 18

或者,application.yml配置如下:

person:
  name: zhangsan
  age: 18

实体Bean代码如下:

@Controller
public class QuickStartController {

    @Value("${person.name}")
    private String name;
    @Value("${person.age}")
    private Integer age;


    @RequestMapping("/quick")
    @ResponseBody
    public String quick(){
        return "springboot 访问成功! name="+name+",age="+age;
    }

}

浏览器访问地址:http://localhost:8080/quick 结果如下:

 

 

4.2.2 使用注解@ConfigurationProperties映射

通过注解@ConfigurationProperties(prefix="配置文件中的key的前缀")可以将配置文件中的配置自动与实体进行映射

application.properties配置如下:

person:
name: zhangsan
age: 18

或者,application.yml配置如下:

person:
name: zhangsan
age: 18

实体Bean代码如下:

@Controller
@ConfigurationProperties(prefix = "person")
public class QuickStartController {
​
   private String name;
   private Integer age;
​
   @RequestMapping("/quick")
   @ResponseBody
   public String quick(){
       return "springboot 访问成功! name="+name+",age="+age;
  }
​
   public void setName(String name) {
       this.name = name;
  }
​
   public void setAge(Integer age) {
       this.age = age;
  }
}

浏览器访问地址:http://localhost:8080/quick 结果如下:

 

注意:使用@ConfigurationProperties方式可以进行配置文件与实体字段的自动映射,但需要字段必须提供set方法才可以,而使用@Value注解修饰的字段不需要提供set方法

 

五、SpringBoot与整合数据库技术

5.1 SpringBoot整合Mybatis

5.1.1 添加Mybatis的起步依赖

<!--mybatis起步依赖-->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.3.2</version>
</dependency>

5.1.2 添加数据库驱动坐标

<!-- MySQL连接驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.11</version>
</dependency>

5.1.3 添加数据库连接信息

在application.properties中添加数据量的连接信息

#DB Configuration:
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test
useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/ShanghaiuseUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=root

5.1.4 创建user表

在test数据库中创建user表

-- ----------------------------
-- Table structure for `user`
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) DEFAULT NULL,
  `password` varchar(50) DEFAULT NULL,
  `name` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'zhangsan', '123', '张三');
INSERT INTO `user` VALUES ('2', 'lisi', '123', '李四');

5.1.5 创建实体Bean

public class User {
    // 主键
    private Long id;
    // 用户名
    private String username;
    // 密码
    private String password;
    // 姓名
    private String name;
  
    //此处省略getter和setter方法 .. ..
    
}

5.1.6 编写Mapper

@Mapper
public interface UserMapper {
	public List<User> queryUserList();
}

注意:@Mapper标记该类是一个mybatis的mapper接口,可以被spring boot自动扫描到spring上下文中

5.1.7 配置Mapper映射文件

在src\main\resources\mapper路径下加入UserMapper.xml配置文件"

<?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.example.demo.mapper.UserMapper">
    <select id="queryUserList" resultType="user">
        select * from user
    </select>
</mapper>

5.1.8 在application.properties中添加mybatis的信息

#spring集成Mybatis环境
#pojo别名扫描包
mybatis.type-aliases-package=com.example.demo.entity
#加载Mybatis映射文件
mybatis.mapper-locations=classpath:mapper/*Mapper.xml
logging:
  level:
    com.example.demo.mapper : debug

5.1.9 编写测试Controller

@Controller
public class MapperController {

    @Autowired
    private UserMapper userMapper;

    @RequestMapping("/queryUser")
    @ResponseBody
    public List<User> queryUser(){
        List<User> users = userMapper.queryUserList();
        return users;
    }

}

5.1.10 测试

 

5.2 SpringBoot整合Junit

5.2.1 添加Junit的起步依赖

<!--测试的起步依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

5.2.2 编写测试类

package com.itheima.test;
​
import com.itheima.MySpringBootApplication;
import com.itheima.domain.User;
import com.itheima.mapper.UserMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
​
import java.util.List;
​
@RunWith(SpringRunner.class)
@SpringBootTest(classes = MySpringBootApplication.class)
public class MapperTest {
​
   @Autowired
   private UserMapper userMapper;
​
   @Test
   public void test() {
       List<User> users = userMapper.queryUserList();
       System.out.println(users);
  }
​
}

其中,

SpringRunner继承自SpringJUnit4ClassRunner,使用哪一个Spring提供的测试测试引擎都可以

public final class SpringRunner extends SpringJUnit4ClassRunner 

@SpringBootTest的属性指定的是引导类的字节码对象

 

5.2.3 控制台打印信息

 

 

5.3 SpringBoot整合Spring Data JPA

5.3.1 添加Spring Data JPA的起步依赖

<!-- springBoot JPA的起步依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

5.3.2 添加数据库驱动依赖

<!-- MySQL连接驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

5.3.3 在application.properties中配置数据库和jpa的相关属性

#DB Configuration:
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=root

#JPA Configuration:
spring.jpa.database=MySQL
spring.jpa.show-sql=true
#spring.jpa.generate-ddl=true
#spring.jpa.hibernate.ddl-auto=update
#spring.jpa.hibernate.naming.physicalstrategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
#spring.jpa.hibernate.naming_strategy=org.hibernate.cfg.ImprovedNamingStrategy

5.3.4 创建实体配置实体

@Entity
public class User {
    // 主键
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    // 用户名
    private String username;
    // 密码
    private String password;
    // 姓名
    private String name;
 
    //此处省略setter和getter方法... ...
}

5.3.5 编写UserRepository

public interface UserRepository extends JpaRepository<User,Long>{
    public List<User> findAll();
}

5.3.6 编写测试类

@RunWith(SpringRunner.class)
@SpringBootTest(classes=MySpringBootApplication.class)
public class JpaTest {

    @Autowired
    private UserRepository userRepository;

    @Test
    public void test(){
        List<User> users = userRepository.findAll();
        System.out.println(users);
    }

}

5.3.7 控制台打印信息

 

注意:如果是jdk9,执行报错如下:

 

原因:jdk缺少相应的jar

解决方案:手动导入对应的maven坐标,如下:

<!--jdk9需要导入如下坐标-->
<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>2.3.0</version>
</dependency>

 

5.4 SpringBoot整合Redis

5.4.1 添加redis的起步依赖

<!-- 配置使用redis启动器 选择使用jedis操作redis -->
<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-data-redis</artifactId>
      <exclusions>
          <exclusion>
               <groupId>io.lettuce</groupId>
               <artifactId>lettuce-core</artifactId>
           </exclusion>
       </exclusions>
</dependency>
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!--除去jackson-databind依赖-->
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</exclusion>
</exclusions>
</dependency>
​

5.4.2 配置redis的连接信息

#Redis
redis:
  password: 123
  port: 6379
  host: localhost
  timeout: 10
  jedis:
    pool:
      max-active: 200 #控制一个pool可分配多少个jedis实例,通过pool.getResource()来获取;如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted。
      max-idle: 10 #资源池允许最大空闲的连接数
      min-idle: 10 #资源池确保最少空闲的连接数
      max-wait: 10000 #表示借用一个jedis实例时,最大的等待时间,如果超过等待时间,则直接抛JedisConnectionException;
​
​

5.4.3 注入RedisTemplate测试redis操作

 

概念:

  Jedis:是Redis的Java实现客户端,提供了比较全面的Redis命令的支持,

  Redisson:实现了分布式和可扩展的Java数据结构。

  Lettuce 【ˈletɪs】:高级Redis客户端,用于线程安全同步,异步和响应使用,支持集群,Sentinel,管道和编码器。

优点:

  Jedis:比较全面的提供了Redis的操作特性

  Redisson:促使使用者对Redis的关注分离,提供很多分布式相关操作服务,例如,分布式锁,分布式集合,可通过Redis支持延迟队列

  Lettuce:主要在一些分布式缓存框架上使用比较多

可伸缩:

Jedis:使用阻塞的I/O,且其方法调用都是同步的,程序流需要等到sockets处理完I/O才能执行,不支持异步。Jedis客户端实例不是线程安全的,所以需要通过连接池来使用Jedis。

Redisson:基于Netty框架的事件驱动的通信层,其方法调用是异步的。Redisson的API是线程安全的,所以可以操作单个Redisson连接来完成各种操作

Lettuce:基于Netty框架的事件驱动的通信层,其方法调用是异步的。Lettuce的API是线程安全的,所以可以操作单个Lettuce连接来完成各种操作


结论:

建议使用:Jedis + Redisson

新建JedisPoolFactory.java

package com.example.demo.redis;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
@Configuration
@PropertySource("classpath:application.yml")
public class JedisPoolFactory {
    @Value("${spring.redis.host}")
    private String host;

    @Value("${spring.redis.port}")
    private int port;

    @Value("${spring.redis.password}")
    private String password;

    @Value("${spring.redis.timeout}")
    private int timeout;

    @Value("${spring.redis.jedis.pool.max-active}")
    private int maxActive;

    @Value("${spring.redis.jedis.pool.max-idle}")
    private int maxIdle;

    @Value("${spring.redis.jedis.pool.min-idle}")
    private int minIdle;

    @Value("${spring.redis.jedis.pool.max-wait}")
    private long maxWaitMillis;


    @Bean
    public JedisPool generateJedisPool(){

        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxIdle(maxIdle);
        jedisPoolConfig.setMinIdle(minIdle);
        jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
        jedisPoolConfig.setMaxTotal(maxActive);
        JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout, password);

        return jedisPool;


    }
}

新建RedisUtil.java

package com.example.demo.redis;
​
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
​
@Component
public class RedisUtil {
​
   @Autowired
   private JedisPool jedisPool;
​
   /**
    * 向Redis中存值,永久有效
    */
   public String set(String key, String value) {
       Jedis jedis = null;
       try {
           jedis = jedisPool.getResource();
           return jedis.set(key, value);
      } catch (Exception e) {
           e.printStackTrace();
           return "0";
      } finally {
           returnResource(jedisPool, jedis);
      }
  }
​
   /**
    * 根据传入Key获取指定Value
    */
   public String get(String key) {
       Jedis jedis = null;
       String value;
       try {
           jedis = jedisPool.getResource();
           value = jedis.get(key);
      } catch (Exception e) {
           e.printStackTrace();
           return "0";
      } finally {
           returnResource(jedisPool, jedis);
      }
       return value;
  }
​
   /**
    * 校验Key值是否存在
    */
   public Boolean exists(String key) {
       Jedis jedis = null;
       try {
           jedis = jedisPool.getResource();
           return jedis.exists(key);
      } catch (Exception e) {
           e.printStackTrace();
           return false;
      } finally {
           returnResource(jedisPool, jedis);
      }
  }
​
   /**
    * 删除指定Key-Value
    */
   public Long delete(String key) {
       Jedis jedis = null;
       try {
           jedis = jedisPool.getResource();
           return jedis.del(key);
      } catch (Exception e) {
           e.printStackTrace();
           return 0L;
      } finally {
           returnResource(jedisPool, jedis);
      }
  }
​
   // 以上为常用方法,更多方法自行百度
​
   /**
    * 释放连接
    */
   private static void returnResource(JedisPool jedisPool, Jedis jedis) {
       if (jedis != null) {
           jedisPool.returnResource(jedis);
      }
  }
}

 

 

5.4.4 添加jedis依赖

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
  <exclusions>
     <exclusion>
        <groupId>io.lettuce</groupId>
        <artifactId>lettuce-core</artifactId>
     </exclusion>
  </exclusions>
</dependency>
<dependency>
  <groupId>redis.clients</groupId>
  <artifactId>jedis</artifactId>
</dependency>

5.4.5 添加redis工具类 RedisUtil

package com.gcs.utils;
​
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
​
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;
​
​
public class RedisUtil {
  private StringRedisTemplate redisTemplate;
​
  public void setRedisTemplate(StringRedisTemplate redisTemplate) {
     this.redisTemplate = redisTemplate;
  }
​
  public StringRedisTemplate getRedisTemplate() {
     return this.redisTemplate;
  }
​
  /** -------------------key相关操作--------------------- */
​
  /**
   * 删除key
   *
   * @param key
   */
  public void delete(String key) {
     redisTemplate.delete(key);
  }
​
  /**
   * 批量删除key
   *
   * @param keys
   */
  public void delete(Collection<String> keys) {
     redisTemplate.delete(keys);
  }
​
  /**
   * 序列化key
   *
   * @param key
   * @return
   */
  public byte[] dump(String key) {
     return redisTemplate.dump(key);
  }
​
  /**
   * 是否存在key
   *
   * @param key
   * @return
   */
  public Boolean hasKey(String key) {
     return redisTemplate.hasKey(key);
  }
​
  /**
   * 设置过期时间
   *
   * @param key
   * @param timeout
   * @param unit
   * @return
   */
  public Boolean expire(String key, long timeout, TimeUnit unit) {
     return redisTemplate.expire(key, timeout, unit);
  }
​
  /**
   * 设置过期时间
   *
   * @param key
   * @param date
   * @return
   */
  public Boolean expireAt(String key, Date date) {
     return redisTemplate.expireAt(key, date);
  }
​
  /**
   * 查找匹配的key
   *
   * @param pattern
   * @return
   */
  public Set<String> keys(String pattern) {
     return redisTemplate.keys(pattern);
  }
​
  /**
   * 将当前数据库的 key 移动到给定的数据库 db 当中
   *
   * @param key
   * @param dbIndex
   * @return
   */
  public Boolean move(String key, int dbIndex) {
     return redisTemplate.move(key, dbIndex);
  }
​
  /**
   * 移除 key 的过期时间,key 将持久保持
   *
   * @param key
   * @return
   */
  public Boolean persist(String key) {
     return redisTemplate.persist(key);
  }
​
  /**
   * 返回 key 的剩余的过期时间
   *
   * @param key
   * @param unit
   * @return
   */
  public Long getExpire(String key, TimeUnit unit) {
     return redisTemplate.getExpire(key, unit);
  }
​
  /**
   * 返回 key 的剩余的过期时间
   *
   * @param key
   * @return
   */
  public Long getExpire(String key) {
     return redisTemplate.getExpire(key);
  }
​
  /**
   * 从当前数据库中随机返回一个 key
   *
   * @return
   */
  public String randomKey() {
     return redisTemplate.randomKey();
  }
​
  /**
   * 修改 key 的名称
   *
   * @param oldKey
   * @param newKey
   */
  public void rename(String oldKey, String newKey) {
     redisTemplate.rename(oldKey, newKey);
  }
​
  /**
   * 仅当 newkey 不存在时,将 oldKey 改名为 newkey
   *
   * @param oldKey
   * @param newKey
   * @return
   */
  public Boolean renameIfAbsent(String oldKey, String newKey) {
     return redisTemplate.renameIfAbsent(oldKey, newKey);
  }
​
  /**
   * 返回 key 所储存的值的类型
   *
   * @param key
   * @return
   */
  public DataType type(String key) {
     return redisTemplate.type(key);
  }
​
  /** -------------------string相关操作--------------------- */
​
  /**
   * 设置指定 key 的值
   * @param key
   * @param value
   */
  public void set(String key, String value) {
     redisTemplate.opsForValue().set(key, value);
  }
​
  /**
   * 获取指定 key 的值
   * @param key
   * @return
   */
  public String get(String key) {
     return redisTemplate.opsForValue().get(key);
  }
​
  /**
   * 返回 key 中字符串值的子字符
   * @param key
   * @param start
   * @param end
   * @return
   */
  public String getRange(String key, long start, long end) {
     return redisTemplate.opsForValue().get(key, start, end);
  }
​
  /**
   * 将给定 key 的值设为 value ,并返回 key 的旧值(old value)
   *
   * @param key
   * @param value
   * @return
   */
  public String getAndSet(String key, String value) {
     return redisTemplate.opsForValue().getAndSet(key, value);
  }
​
  /**
   * 对 key 所储存的字符串值,获取指定偏移量上的位(bit)
   *
   * @param key
   * @param offset
   * @return
   */
  public Boolean getBit(String key, long offset) {
     return redisTemplate.opsForValue().getBit(key, offset);
  }
​
  /**
   * 批量获取
   *
   * @param keys
   * @return
   */
  public List<String> multiGet(Collection<String> keys) {
     return redisTemplate.opsForValue().multiGet(keys);
  }
​
  /**
   * 设置ASCII码, 字符串'a'的ASCII码是97, 转为二进制是'01100001', 此方法是将二进制第offset位值变为value
   *
   * @param key
   * @param postion
   *           位置
   * @param value
   *           值,true为1, false为0
   * @return
   */
  public boolean setBit(String key, long offset, boolean value) {
     return redisTemplate.opsForValue().setBit(key, offset, value);
  }
​
  /**
   * 将值 value 关联到 key ,并将 key 的过期时间设为 timeout
   *
   * @param key
   * @param value
   * @param timeout
   *           过期时间
   * @param unit
   *           时间单位, 天:TimeUnit.DAYS 小时:TimeUnit.HOURS 分钟:TimeUnit.MINUTES
   *           秒:TimeUnit.SECONDS 毫秒:TimeUnit.MILLISECONDS
   */
  public void setEx(String key, String value, long timeout, TimeUnit unit) {
     redisTemplate.opsForValue().set(key, value, timeout, unit);
  }
​
  /**
   * 只有在 key 不存在时设置 key 的值
   *
   * @param key
   * @param value
   * @return 之前已经存在返回false,不存在返回true
   */
  public boolean setIfAbsent(String key, String value) {
     return redisTemplate.opsForValue().setIfAbsent(key, value);
  }
​
  /**
   * 用 value 参数覆写给定 key 所储存的字符串值,从偏移量 offset 开始
   *
   * @param key
   * @param value
   * @param offset
   *           从指定位置开始覆写
   */
  public void setRange(String key, String value, long offset) {
     redisTemplate.opsForValue().set(key, value, offset);
  }
​
  /**
   * 获取字符串的长度
   *
   * @param key
   * @return
   */
  public Long size(String key) {
     return redisTemplate.opsForValue().size(key);
  }
​
  /**
   * 批量添加
   *
   * @param maps
   */
  public void multiSet(Map<String, String> maps) {
     redisTemplate.opsForValue().multiSet(maps);
  }
​
  /**
   * 同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在
   *
   * @param maps
   * @return 之前已经存在返回false,不存在返回true
   */
  public boolean multiSetIfAbsent(Map<String, String> maps) {
     return redisTemplate.opsForValue().multiSetIfAbsent(maps);
  }
​
  /**
   * 增加(自增长), 负数则为自减
   *
   * @param key
   * @param value
   * @return
   */
  public Long incrBy(String key, long increment) {
     return redisTemplate.opsForValue().increment(key, increment);
  }
​
  /**
   *
   * @param key
   * @param value
   * @return
   */
  public Double incrByFloat(String key, double increment) {
     return redisTemplate.opsForValue().increment(key, increment);
  }
​
  /**
   * 追加到末尾
   *
   * @param key
   * @param value
   * @return
   */
  public Integer append(String key, String value) {
     return redisTemplate.opsForValue().append(key, value);
  }
​
  /** -------------------hash相关操作------------------------- */
​
  /**
   * 获取存储在哈希表中指定字段的值
   *
   * @param key
   * @param field
   * @return
   */
  public Object hGet(String key, String field) {
     return redisTemplate.opsForHash().get(key, field);
  }
​
  /**
   * 获取所有给定字段的值
   *
   * @param key
   * @return
   */
  public Map<Object, Object> hGetAll(String key) {
     return redisTemplate.opsForHash().entries(key);
  }
​
  /**
   * 获取所有给定字段的值
   *
   * @param key
   * @param fields
   * @return
   */
  public List<Object> hMultiGet(String key, Collection<Object> fields) {
     return redisTemplate.opsForHash().multiGet(key, fields);
  }
​
  public void hPut(String key, String hashKey, String value) {
     redisTemplate.opsForHash().put(key, hashKey, value);
  }
​
  public void hPutAll(String key, Map<String, String> maps) {
     redisTemplate.opsForHash().putAll(key, maps);
  }
​
  /**
   * 仅当hashKey不存在时才设置
   *
   * @param key
   * @param hashKey
   * @param value
   * @return
   */
  public Boolean hPutIfAbsent(String key, String hashKey, String value) {
     return redisTemplate.opsForHash().putIfAbsent(key, hashKey, value);
  }
​
  /**
   * 删除一个或多个哈希表字段
   *
   * @param key
   * @param fields
   * @return
   */
  public Long hDelete(String key, Object... fields) {
     return redisTemplate.opsForHash().delete(key, fields);
  }
​
  /**
   * 查看哈希表 key 中,指定的字段是否存在
   *
   * @param key
   * @param field
   * @return
   */
  public boolean hExists(String key, String field) {
     return redisTemplate.opsForHash().hasKey(key, field);
  }
​
  /**
   * 为哈希表 key 中的指定字段的整数值加上增量 increment
   *
   * @param key
   * @param field
   * @param increment
   * @return
   */
  public Long hIncrBy(String key, Object field, long increment) {
     return redisTemplate.opsForHash().increment(key, field, increment);
  }
​
  /**
   * 为哈希表 key 中的指定字段的整数值加上增量 increment
   *
   * @param key
   * @param field
   * @param delta
   * @return
   */
  public Double hIncrByFloat(String key, Object field, double delta) {
     return redisTemplate.opsForHash().increment(key, field, delta);
  }
​
  /**
   * 获取所有哈希表中的字段
   *
   * @param key
   * @return
   */
  public Set<Object> hKeys(String key) {
     return redisTemplate.opsForHash().keys(key);
  }
​
  /**
   * 获取哈希表中字段的数量
   *
   * @param key
   * @return
   */
  public Long hSize(String key) {
     return redisTemplate.opsForHash().size(key);
  }
​
  /**
   * 获取哈希表中所有值
   *
   * @param key
   * @return
   */
  public List<Object> hValues(String key) {
     return redisTemplate.opsForHash().values(key);
  }
​
  /**
   * 迭代哈希表中的键值对
   *
   * @param key
   * @param options
   * @return
   */
  public Cursor<Entry<Object, Object>> hScan(String key, ScanOptions options) {
     return redisTemplate.opsForHash().scan(key, options);
  }
​
  /** ------------------------list相关操作---------------------------- */
​
  /**
   * 通过索引获取列表中的元素
   *
   * @param key
   * @param index
   * @return
   */
  public String lIndex(String key, long index) {
     return redisTemplate.opsForList().index(key, index);
  }
​
  /**
   * 获取列表指定范围内的元素
   *
   * @param key
   * @param start
   *           开始位置, 0是开始位置
   * @param end
   *           结束位置, -1返回所有
   * @return
   */
  public List<String> lRange(String key, long start, long end) {
     return redisTemplate.opsForList().range(key, start, end);
  }
​
  /**
   * 存储在list头部
   *
   * @param key
   * @param value
   * @return
   */
  public Long lLeftPush(String key, String value) {
     return redisTemplate.opsForList().leftPush(key, value);
  }
​
  /**
   *
   * @param key
   * @param value
   * @return
   */
  public Long lLeftPushAll(String key, String... value) {
     return redisTemplate.opsForList().leftPushAll(key, value);
  }
​
  /**
   *
   * @param key
   * @param value
   * @return
   */
  public Long lLeftPushAll(String key, Collection<String> value) {
     return redisTemplate.opsForList().leftPushAll(key, value);
  }
​
  /**
   * 当list存在的时候才加入
   *
   * @param key
   * @param value
   * @return
   */
  public Long lLeftPushIfPresent(String key, String value) {
     return redisTemplate.opsForList().leftPushIfPresent(key, value);
  }
​
  /**
   * 如果pivot存在,再pivot前面添加
   *
   * @param key
   * @param pivot
   * @param value
   * @return
   */
  public Long lLeftPush(String key, String pivot, String value) {
     return redisTemplate.opsForList().leftPush(key, pivot, value);
  }
​
  /**
   *
   * @param key
   * @param value
   * @return
   */
  public Long lRightPush(String key, String value) {
     return redisTemplate.opsForList().rightPush(key, value);
  }
​
  /**
   *
   * @param key
   * @param value
   * @return
   */
  public Long lRightPushAll(String key, String... value) {
     return redisTemplate.opsForList().rightPushAll(key, value);
  }
​
  /**
   *
   * @param key
   * @param value
   * @return
   */
  public Long lRightPushAll(String key, Collection<String> value) {
     return redisTemplate.opsForList().rightPushAll(key, value);
  }
​
  /**
   * 为已存在的列表添加值
   *
   * @param key
   * @param value
   * @return
   */
  public Long lRightPushIfPresent(String key, String value) {
     return redisTemplate.opsForList().rightPushIfPresent(key, value);
  }
​
  /**
   * 在pivot元素的右边添加值
   *
   * @param key
   * @param pivot
   * @param value
   * @return
   */
  public Long lRightPush(String key, String pivot, String value) {
     return redisTemplate.opsForList().rightPush(key, pivot, value);
  }
​
  /**
   * 通过索引设置列表元素的值
   *
   * @param key
   * @param index
   *           位置
   * @param value
   */
  public void lSet(String key, long index, String value) {
     redisTemplate.opsForList().set(key, index, value);
  }
​
  /**
   * 移出并获取列表的第一个元素
   *
   * @param key
   * @return 删除的元素
   */
  public String lLeftPop(String key) {
     return redisTemplate.opsForList().leftPop(key);
  }
​
  /**
   * 移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
   *
   * @param key
   * @param timeout
   *           等待时间
   * @param unit
   *           时间单位
   * @return
   */
  public String lBLeftPop(String key, long timeout, TimeUnit unit) {
     return redisTemplate.opsForList().leftPop(key, timeout, unit);
  }
​
  /**
   * 移除并获取列表最后一个元素
   *
   * @param key
   * @return 删除的元素
   */
  public String lRightPop(String key) {
     return redisTemplate.opsForList().rightPop(key);
  }
​
  /**
   * 移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
   *
   * @param key
   * @param timeout
   *           等待时间
   * @param unit
   *           时间单位
   * @return
   */
  public String lBRightPop(String key, long timeout, TimeUnit unit) {
     return redisTemplate.opsForList().rightPop(key, timeout, unit);
  }
​
  /**
   * 移除列表的最后一个元素,并将该元素添加到另一个列表并返回
   *
   * @param sourceKey
   * @param destinationKey
   * @return
   */
  public String lRightPopAndLeftPush(String sourceKey, String destinationKey) {
     return redisTemplate.opsForList().rightPopAndLeftPush(sourceKey,
           destinationKey);
  }
​
  /**
   * 从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它; 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
   *
   * @param sourceKey
   * @param destinationKey
   * @param timeout
   * @param unit
   * @return
   */
  public String lBRightPopAndLeftPush(String sourceKey, String destinationKey,
        long timeout, TimeUnit unit) {
     return redisTemplate.opsForList().rightPopAndLeftPush(sourceKey,
           destinationKey, timeout, unit);
  }
​
  /**
   * 删除集合中值等于value得元素
   *
   * @param key
   * @param index
   *           index=0, 删除所有值等于value的元素; index>0, 从头部开始删除第一个值等于value的元素;
   *           index<0, 从尾部开始删除第一个值等于value的元素;
   * @param value
   * @return
   */
  public Long lRemove(String key, long index, String value) {
     return redisTemplate.opsForList().remove(key, index, value);
  }
​
  /**
   * 裁剪list
   *
   * @param key
   * @param start
   * @param end
   */
  public void lTrim(String key, long start, long end) {
     redisTemplate.opsForList().trim(key, start, end);
  }
​
  /**
   * 获取列表长度
   *
   * @param key
   * @return
   */
  public Long lLen(String key) {
     return redisTemplate.opsForList().size(key);
  }
​
  /** --------------------set相关操作-------------------------- */
​
  /**
   * set添加元素
   *
   * @param key
   * @param values
   * @return
   */
  public Long sAdd(String key, String... values) {
     return redisTemplate.opsForSet().add(key, values);
  }
​
  /**
   * set移除元素
   *
   * @param key
   * @param values
   * @return
   */
  public Long sRemove(String key, Object... values) {
     return redisTemplate.opsForSet().remove(key, values);
  }
​
  /**
   * 移除并返回集合的一个随机元素
   *
   * @param key
   * @return
   */
  public String sPop(String key) {
     return redisTemplate.opsForSet().pop(key);
  }
​
  /**
   * 将元素value从一个集合移到另一个集合
   *
   * @param key
   * @param value
   * @param destKey
   * @return
   */
  public Boolean sMove(String key, String value, String destKey) {
     return redisTemplate.opsForSet().move(key, value, destKey);
  }
​
  /**
   * 获取集合的大小
   *
   * @param key
   * @return
   */
  public Long sSize(String key) {
     return redisTemplate.opsForSet().size(key);
  }
​
  /**
   * 判断集合是否包含value
   *
   * @param key
   * @param value
   * @return
   */
  public Boolean sIsMember(String key, Object value) {
     return redisTemplate.opsForSet().isMember(key, value);
  }
​
  /**
   * 获取两个集合的交集
   *
   * @param key
   * @param otherKey
   * @return
   */
  public Set<String> sIntersect(String key, String otherKey) {
     return redisTemplate.opsForSet().intersect(key, otherKey);
  }
​
  /**
   * 获取key集合与多个集合的交集
   *
   * @param key
   * @param otherKeys
   * @return
   */
  public Set<String> sIntersect(String key, Collection<String> otherKeys) {
     return redisTemplate.opsForSet().intersect(key, otherKeys);
  }
​
  /**
   * key集合与otherKey集合的交集存储到destKey集合中
   *
   * @param key
   * @param otherKey
   * @param destKey
   * @return
   */
  public Long sIntersectAndStore(String key, String otherKey, String destKey) {
     return redisTemplate.opsForSet().intersectAndStore(key, otherKey,
           destKey);
  }
​
  /**
   * key集合与多个集合的交集存储到destKey集合中
   *
   * @param key
   * @param otherKeys
   * @param destKey
   * @return
   */
  public Long sIntersectAndStore(String key, Collection<String> otherKeys,
        String destKey) {
     return redisTemplate.opsForSet().intersectAndStore(key, otherKeys,
           destKey);
  }
​
  /**
   * 获取两个集合的并集
   *
   * @param key
   * @param otherKeys
   * @return
   */
  public Set<String> sUnion(String key, String otherKeys) {
     return redisTemplate.opsForSet().union(key, otherKeys);
  }
​
  /**
   * 获取key集合与多个集合的并集
   *
   * @param key
   * @param otherKeys
   * @return
   */
  public Set<String> sUnion(String key, Collection<String> otherKeys) {
     return redisTemplate.opsForSet().union(key, otherKeys);
  }
​
  /**
   * key集合与otherKey集合的并集存储到destKey中
   *
   * @param key
   * @param otherKey
   * @param destKey
   * @return
   */
  public Long sUnionAndStore(String key, String otherKey, String destKey) {
     return redisTemplate.opsForSet().unionAndStore(key, otherKey, destKey);
  }
​
  /**
   * key集合与多个集合的并集存储到destKey中
   *
   * @param key
   * @param otherKeys
   * @param destKey
   * @return
   */
  public Long sUnionAndStore(String key, Collection<String> otherKeys,
        String destKey) {
     return redisTemplate.opsForSet().unionAndStore(key, otherKeys, destKey);
  }
​
  /**
   * 获取两个集合的差集
   *
   * @param key
   * @param otherKey
   * @return
   */
  public Set<String> sDifference(String key, String otherKey) {
     return redisTemplate.opsForSet().difference(key, otherKey);
  }
​
  /**
   * 获取key集合与多个集合的差集
   *
   * @param key
   * @param otherKeys
   * @return
   */
  public Set<String> sDifference(String key, Collection<String> otherKeys) {
     return redisTemplate.opsForSet().difference(key, otherKeys);
  }
​
  /**
   * key集合与otherKey集合的差集存储到destKey中
   *
   * @param key
   * @param otherKey
   * @param destKey
   * @return
   */
  public Long sDifference(String key, String otherKey, String destKey) {
     return redisTemplate.opsForSet().differenceAndStore(key, otherKey,
           destKey);
  }
​
  /**
   * key集合与多个集合的差集存储到destKey中
   *
   * @param key
   * @param otherKeys
   * @param destKey
   * @return
   */
  public Long sDifference(String key, Collection<String> otherKeys,
        String destKey) {
     return redisTemplate.opsForSet().differenceAndStore(key, otherKeys,
           destKey);
  }
​
  /**
   * 获取集合所有元素
   *
   * @param key
   * @param otherKeys
   * @param destKey
   * @return
   */
  public Set<String> setMembers(String key) {
     return redisTemplate.opsForSet().members(key);
  }
​
  /**
   * 随机获取集合中的一个元素
   *
   * @param key
   * @return
   */
  public String sRandomMember(String key) {
     return redisTemplate.opsForSet().randomMember(key);
  }
​
  /**
   * 随机获取集合中count个元素
   *
   * @param key
   * @param count
   * @return
   */
  public List<String> sRandomMembers(String key, long count) {
     return redisTemplate.opsForSet().randomMembers(key, count);
  }
​
  /**
   * 随机获取集合中count个元素并且去除重复的
   *
   * @param key
   * @param count
   * @return
   */
  public Set<String> sDistinctRandomMembers(String key, long count) {
     return redisTemplate.opsForSet().distinctRandomMembers(key, count);
  }
​
  /**
   *
   * @param key
   * @param options
   * @return
   */
  public Cursor<String> sScan(String key, ScanOptions options) {
     return redisTemplate.opsForSet().scan(key, options);
  }
​
  /**------------------zSet相关操作--------------------------------*/
 
  /**
   * 添加元素,有序集合是按照元素的score值由小到大排列
   *
   * @param key
   * @param value
   * @param score
   * @return
   */
  public Boolean zAdd(String key, String value, double score) {
     return redisTemplate.opsForZSet().add(key, value, score);
  }
​
  /**
   *
   * @param key
   * @param values
   * @return
   */
  public Long zAdd(String key, Set<TypedTuple<String>> values) {
     return redisTemplate.opsForZSet().add(key, values);
  }
​
  /**
   *
   * @param key
   * @param values
   * @return
   */
  public Long zRemove(String key, Object... values) {
     return redisTemplate.opsForZSet().remove(key, values);
  }
​
  /**
   * 增加元素的score值,并返回增加后的值
   *
   * @param key
   * @param value
   * @param delta
   * @return
   */
  public Double zIncrementScore(String key, String value, double delta) {
     return redisTemplate.opsForZSet().incrementScore(key, value, delta);
  }
​
  /**
   * 返回元素在集合的排名,有序集合是按照元素的score值由小到大排列
   *
   * @param key
   * @param value
   * @return 0表示第一位
   */
  public Long zRank(String key, Object value) {
     return redisTemplate.opsForZSet().rank(key, value);
  }
​
  /**
   * 返回元素在集合的排名,按元素的score值由大到小排列
   *
   * @param key
   * @param value
   * @return
   */
  public Long zReverseRank(String key, Object value) {
     return redisTemplate.opsForZSet().reverseRank(key, value);
  }
​
  /**
   * 获取集合的元素, 从小到大排序
   *
   * @param key
   * @param start
   *           开始位置
   * @param end
   *           结束位置, -1查询所有
   * @return
   */
  public Set<String> zRange(String key, long start, long end) {
     return redisTemplate.opsForZSet().range(key, start, end);
  }
​
  /**
   * 获取集合元素, 并且把score值也获取
   *
   * @param key
   * @param start
   * @param end
   * @return
   */
  public Set<TypedTuple<String>> zRangeWithScores(String key, long start,
        long end) {
     return redisTemplate.opsForZSet().rangeWithScores(key, start, end);
  }
​
  /**
   * 根据Score值查询集合元素
   *
   * @param key
   * @param min
   *           最小值
   * @param max
   *           最大值
   * @return
   */
  public Set<String> zRangeByScore(String key, double min, double max) {
     return redisTemplate.opsForZSet().rangeByScore(key, min, max);
  }
​
  /**
   * 根据Score值查询集合元素, 从小到大排序
   *
   * @param key
   * @param min
   *           最小值
   * @param max
   *           最大值
   * @return
   */
  public Set<TypedTuple<String>> zRangeByScoreWithScores(String key,
        double min, double max) {
     return redisTemplate.opsForZSet().rangeByScoreWithScores(key, min, max);
  }
​
  /**
   *
   * @param key
   * @param min
   * @param max
   * @param start
   * @param end
   * @return
   */
  public Set<TypedTuple<String>> zRangeByScoreWithScores(String key,
        double min, double max, long start, long end) {
     return redisTemplate.opsForZSet().rangeByScoreWithScores(key, min, max,
           start, end);
  }
​
  /**
   * 获取集合的元素, 从大到小排序
   *
   * @param key
   * @param start
   * @param end
   * @return
   */
  public Set<String> zReverseRange(String key, long start, long end) {
     return redisTemplate.opsForZSet().reverseRange(key, start, end);
  }
​
  /**
   * 获取集合的元素, 从大到小排序, 并返回score值
   *
   * @param key
   * @param start
   * @param end
   * @return
   */
  public Set<TypedTuple<String>> zReverseRangeWithScores(String key,
        long start, long end) {
     return redisTemplate.opsForZSet().reverseRangeWithScores(key, start,
           end);
  }
​
  /**
   * 根据Score值查询集合元素, 从大到小排序
   *
   * @param key
   * @param min
   * @param max
   * @return
   */
  public Set<String> zReverseRangeByScore(String key, double min,
        double max) {
     return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max);
  }
​
  /**
   * 根据Score值查询集合元素, 从大到小排序
   *
   * @param key
   * @param min
   * @param max
   * @return
   */
  public Set<TypedTuple<String>> zReverseRangeByScoreWithScores(
        String key, double min, double max) {
     return redisTemplate.opsForZSet().reverseRangeByScoreWithScores(key,
           min, max);
  }
​
  /**
   *
   * @param key
   * @param min
   * @param max
   * @param start
   * @param end
   * @return
   */
  public Set<String> zReverseRangeByScore(String key, double min,
        double max, long start, long end) {
     return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max,
           start, end);
  }
​
  /**
   * 根据score值获取集合元素数量
   *
   * @param key
   * @param min
   * @param max
   * @return
   */
  public Long zCount(String key, double min, double max) {
     return redisTemplate.opsForZSet().count(key, min, max);
  }
​
  /**
   * 获取集合大小
   *
   * @param key
   * @return
   */
  public Long zSize(String key) {
     return redisTemplate.opsForZSet().size(key);
  }
​
  /**
   * 获取集合大小
   *
   * @param key
   * @return
   */
  public Long zZCard(String key) {
     return redisTemplate.opsForZSet().zCard(key);
  }
​
  /**
   * 获取集合中value元素的score值
   *
   * @param key
   * @param value
   * @return
   */
  public Double zScore(String key, Object value) {
     return redisTemplate.opsForZSet().score(key, value);
  }
​
  /**
   * 移除指定索引位置的成员
   *
   * @param key
   * @param start
   * @param end
   * @return
   */
  public Long zRemoveRange(String key, long start, long end) {
     return redisTemplate.opsForZSet().removeRange(key, start, end);
  }
​
  /**
   * 根据指定的score值的范围来移除成员
   *
   * @param key
   * @param min
   * @param max
   * @return
   */
  public Long zRemoveRangeByScore(String key, double min, double max) {
     return redisTemplate.opsForZSet().removeRangeByScore(key, min, max);
  }
​
  /**
   * 获取key和otherKey的并集并存储在destKey中
   *
   * @param key
   * @param otherKey
   * @param destKey
   * @return
   */
  public Long zUnionAndStore(String key, String otherKey, String destKey) {
     return redisTemplate.opsForZSet().unionAndStore(key, otherKey, destKey);
  }
​
  /**
   *
   * @param key
   * @param otherKeys
   * @param destKey
   * @return
   */
  public Long zUnionAndStore(String key, Collection<String> otherKeys,
        String destKey) {
     return redisTemplate.opsForZSet()
          .unionAndStore(key, otherKeys, destKey);
  }
​
  /**
   * 交集
   *
   * @param key
   * @param otherKey
   * @param destKey
   * @return
   */
  public Long zIntersectAndStore(String key, String otherKey,
        String destKey) {
     return redisTemplate.opsForZSet().intersectAndStore(key, otherKey,
           destKey);
  }
​
  /**
   * 交集
   *
   * @param key
   * @param otherKeys
   * @param destKey
   * @return
   */
  public Long zIntersectAndStore(String key, Collection<String> otherKeys,
        String destKey) {
     return redisTemplate.opsForZSet().intersectAndStore(key, otherKeys,
           destKey);
  }
​
  /**
   *
   * @param key
   * @param options
   * @return
   */
  public Cursor<TypedTuple<String>> zScan(String key, ScanOptions options) {
     return redisTemplate.opsForZSet().scan(key, options);
  }
}

修改UserController.java

@GetMapping("login")
public String login1(String username,String password){
    String s = redisUtil.get(username);

  // System.out.println(s);


    if(s==null){
        User user = userMapper.login(username, password);
        if(user!=null&&!user.equals("")){

            String ss = redisUtil.set(username,   JSON.toJSONString(user));
            System.out.println("--------------"+ss);
            return "success";
        }else{
            return "用户名或密码错误";
        }

    }else{
        User user = JSON.parseObject(s,User.class);
        if(user.getPassword().equals(password)){

            return "success";
        }


    }

    
return  "error";

}

加入fastJson

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
   <!--除去jackson-databind依赖-->
   <exclusions>
      <exclusion>
         <groupId>com.fasterxml.jackson.core</groupId>
         <artifactId>jackson-databind</artifactId>
      </exclusion>
   </exclusions>
</dependency>
<!--引入fastjson依赖-->
<dependency>
   <groupId>com.alibaba</groupId>
   <artifactId>fastjson</artifactId>
   <version>1.2.47</version>
</dependency>

新建fastJson工具类

package com.example.demo.redis;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.JSONLibDataFormatSerializer;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;

import java.util.List;
import java.util.Map;

public class FastJsonUtil {
    private static final SerializeConfig config;

    static {
        config = new SerializeConfig();
        config.put(java.util.Date.class, new JSONLibDataFormatSerializer()); // 使用和json-lib兼容的日期输出格式
        config.put(java.sql.Date.class, new JSONLibDataFormatSerializer()); // 使用和json-lib兼容的日期输出格式
    }

    private static final SerializerFeature[] features = {SerializerFeature.WriteMapNullValue, // 输出空置字段
            SerializerFeature.WriteNullListAsEmpty, // list字段如果为null,输出为[],而不是null
            SerializerFeature.WriteNullNumberAsZero, // 数值字段如果为null,输出为0,而不是null
            SerializerFeature.WriteNullBooleanAsFalse, // Boolean字段如果为null,输出为false,而不是null
            SerializerFeature.WriteNullStringAsEmpty // 字符类型字段如果为null,输出为"",而不是null
    };


    public static String convertObjectToJSON(Object object) {
        return JSON.toJSONString(object, config, features);
    }

    public static String toJSONNoFeatures(Object object) {
        return JSON.toJSONString(object, config);
    }



    public static Object toBean(String text) {
        return JSON.parse(text);
    }

    public static <T> T toBean(String text, Class<T> clazz) {
        return JSON.parseObject(text, clazz);
    }

    /**
     *  转换为数组
     * @param text
     * @return
     */
    public static <T> Object[] toArray(String text) {
        return toArray(text, null);
    }

    /**
     *  转换为数组
     * @param text
     * @param clazz
     * @return
     */
    public static <T> Object[] toArray(String text, Class<T> clazz) {
        return JSON.parseArray(text, clazz).toArray();
    }

    /**
     * 转换为List
     * @param text
     * @param clazz
     * @return
     */
    public static <T> List<T> toList(String text, Class<T> clazz) {
        return JSON.parseArray(text, clazz);
    }

    /**
     * 将string转化为序列化的json字符串
     * @param
     * @return
     */
    public static Object textToJson(String text) {
        Object objectJson  = JSON.parse(text);
        return objectJson;
    }

    /**
     * json字符串转化为map
     * @param s
     * @return
     */
    public static <K, V> Map<K, V>  stringToCollect(String s) {
        Map<K, V> m = (Map<K, V>) JSONObject.parseObject(s);
        return m;
    }

    /**
     * 转换JSON字符串为对象
     * @param jsonData
     * @param clazz
     * @return
     */
    public static Object convertJsonToObject(String jsonData, Class<?> clazz) {
        return JSONObject.parseObject(jsonData, clazz);
    }

    /**
     * 将map转化为string
     * @param m
     * @return
     */
    public static <K, V> String collectToString(Map<K, V> m) {
        String s = JSONObject.toJSONString(m);
        return s;
    }
}

 

测试**

http://localhost:8080/login?username=zy&password=6

六、SpringBoot整合web技术

springboot 是内嵌web容器的,推荐打成jar包不是war包

你如果想使用jsp也可以啊,自建WEB-INF web.xml 然后像往常一样使用jsp

打成war包使用外部容器即可,这就相当于失去了一些springboot的特性了

 

至于模板引擎其实都差不多,jsp也可以算是模板引擎,springboot推荐的thymeleaf主要嵌入到html的标签属性,这样对前端很友好,有一些优点,但和jsp,FreeMarker比起来也没什么绝对性的优势。

现在web前端技术飞一样的发展,前后端分离才是主流,模板引擎说白了还是给后端人员用的东西,早就不符合现代的前端思想了,而且和React、Vue这类的前端框架比较流行。

 

 

Thymeleaf是一个流行的模板引擎,该模板引擎采用Java语言开发,模板引擎是一个技术名词,是跨领域跨平台的概念,在Java语言体系下有模板引擎,在C#、PHP语言体系下也有模板引擎。除了thymeleaf之外还有Velocity、FreeMarker等模板引擎,功能类似。

Thymeleaf的主要目标在于提供一种可被浏览器正确显示的、格式良好的模板创建方式,因此也可以用作静态建模。你可以使用它创建经过验证的XML与HTML模板。使用thymeleaf创建的html模板可以在浏览器里面直接打开(展示静态数据),这有利于前后端分离。需要注意的是thymeleaf不是spring旗下的。

6.1 SpringBoot集成thymeleaf

6.1.1 添加thymeleaf依赖

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

6.1.2 添加配置信息

spring:
  thymeleaf:
    enabled: true  #开启thymeleaf视图解析
    encoding: utf-8  #编码
    prefix: classpath:/templates/  #前缀
    cache: false  #是否使用缓存
    mode: HTML  #严格的HTML语法模式
    suffix: .html  #后缀名
    servlet:
      content-type: text/html #content-type设置

6.1.3 /templates目录下创建users.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>用户列表</title>
</head>
<body>
<table>
    <tr>
        <td>ID</td>
        <td>姓名</td>
        <td>密码</td>
    </tr>
    <tr th:each="user:${users}">
        <td th:text="${user.id}"></td>
        <td th:text="${user.username}"></td>
        <td th:text="${user.password}"></td>
    </tr>
</table>
</body>
</html>

6.1.4 修改UserController.java

@GetMapping("/users")
public ModelAndView getUsers(){
    List<User> users = userMapper.getUsers();
    ModelAndView mv = new ModelAndView();
    mv.addObject("users",users);
    mv.setViewName("users");
    return mv;
}

6.1.5测试

http://localhost:8080/users

 

6.2 SpringBoot集成freemarker

FreeMarker是一款模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页、电子邮件、配置文件、源代码等)的通用工具。 它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。

FreeMarker是免费的,基于Apache许可证2.0版本发布。其模板编写为FreeMarker Template Language(FTL),属于简单、专用的语言。需要准备数据在真实编程语言中来显示,比如数据库查询和业务运算, 之后模板显示已经准备好的数据。在模板中,主要用于如何展现数据, 而在模板之外注意于要展示什么数据。

它是一个工具,基于模板生成文本输出的通用工具,使用java编写。与具体的容器无关,不知道HTTP或Servlet;所以他可以应用在非Web应用程序环境中。就像是一个工厂,原材料(比如数据和文字)交给FreeMarker这个工厂,它就会将这些材料组装在一起生成一个我们需要的产品。

它本身是一种模板语言,有自己的语法,但是他是脱离servlet容器独立运行的,所以在上面才会说它是一个工具,与具体的容器无关,允许程序员将其嵌入到我们的系统中。

 

 

6.2.1 添加freemarker依赖

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

6.2.2 添加配置信息

freemarker:
  content-type: text/html
  charset: UTF-8
  template-loader-path: classpath:/templates
  suffix: .ftl

6.2.3 /templates目录下创建users.ftl

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<table border="1">
    <tr>
        <td>ID</td>
        <td>姓名</td>
        <td>密码</td>
    </tr>
    
    <#if users ?? &&(users?size>0)>
    <#list users as user>
    <tr>
        <td>${user.id}</td>
        <td>${user.username}</td>
        <td>${user.password}</td>
    </tr>
    </#list>
   </#if>

</table>
</body>
</html>

 

6.2.4 测试(UserController不用变)

http://localhost:8080/users

七、SpringBoot企业开发

7.1 spring集成Swagger2

 

swagger的由来

  • 随着互联网技术的发展,现在的网站架构基本都由原来的后端渲染,变成了:前端渲染、先后端分离的形态,而且前端技术和后端技术在各自的道路上越走越远。

  • 前端和后端的唯一联系,变成了API接口;API文档变成了前后端开发人员联系的纽带,变得越来越重要,swagger就是一款让你更好的书写API文档的框架。

同类技术

 

 

7.1.1 添加swagger2依赖

<dependency>
   <groupId>io.springfox</groupId>
   <artifactId>springfox-swagger2</artifactId>
   <version>2.9.2</version>
</dependency>
<dependency>
   <groupId>io.springfox</groupId>
   <artifactId>springfox-swagger-ui</artifactId>
   <version>2.9.2</version>
</dependency>

 

7.1.2 添加配置文件

@Configuration
@EnableSwagger2
public class SwaggerConfig {
    @Bean
    public Docket createRestApi() {
        ApiInfo apiInfo = new ApiInfoBuilder()
                .title("服务接口文档范例")
                .description("服务接口文档,严格遵循RESTful API设计规范。")
                .contact("程就人生")
                .version("1.0")
                .build();

        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo)
                .select()
                //以扫描包的方式
                .apis(RequestHandlerSelectors.basePackage("com.example.demo.controller"))
                .paths(PathSelectors.any())
                .build();
    }


}

7.1.3 修改controller

@RestController
@Api(tags = "角色controller")
public class Controller2 {
    @Value("${person.name}")
    private String name;
    @Value("${person.age}")
    private int age;



@GetMapping("getRoles")
@ApiOperation(value = "测试角色")
@ApiImplicitParam(name = "roleName", value = "角色名", required = true,
        dataType = "string", paramType = "query", defaultValue = "普通用户")
    public String getRole(String roleName){
    return "success!"+roleName;
}
}
   // https://gumutianqi1.gitbooks.io/specification-doc/content/tools-doc/spring-boot-swagger2-guide.html

7.1.4查看效果

http://localhost:8080/swagger-ui.html

7.2 springBoot 和定时任务

7.2.1 添加web依赖

7.2.2 添加MyScheduled 添加@Scheduled注解

@Component
public class MySchedule {
    @Scheduled(fixedRate = 5000) //五秒执行一次
    public void test1(){
        System.out.println(new Date());
    }
    @Scheduled(fixedDelay = 2000) //两秒后执行
    public void test2(){
        System.out.println("22222222222222222222");
    }
    @Scheduled(cron = "0 0/1 * * * ?")
    public void test3(){
        System.out.println("每分钟执行一次");
    }
}

7.2.3 在启动类中加入@EnableScheduling注解

@SpringBootApplication
@EnableScheduling
public class DemoApplication {

   public static void main(String[] args) {
      SpringApplication.run(DemoApplication.class, args);
   }
}

7.2.4 测试效果

直接运行

7.2.5 cron表达式

Cron表达式是一个字符串,字符串以5或6个空格隔开,分为6或7个域,每一个域代表一个含义,Cron有如下两种语法格式:

  (1) Seconds Minutes Hours DayofMonth Month DayofWeek Year

  (2)Seconds Minutes Hours DayofMonth Month DayofWeek

  

  一、结构

  corn从左到右(用空格隔开):秒 分 小时 月份中的日期 月份 星期中的日期 年份

  二、各字段的含义

字段允许值允许的特殊字符
秒(Seconds) 0~59的整数 , - * / 四个字符
分(Minutes) 0~59的整数 , - * / 四个字符
小时(Hours) 0~23的整数 , - * / 四个字符
日期(DayofMonth) 1~31的整数(但是你需要考虑你月的天数) ,- * ? / L W C 八个字符
月份(Month) 1~12的整数或者 JAN-DEC , - * / 四个字符
星期(DayofWeek) 1~7的整数或者 SUN-SAT (1=SUN) , - * ? / L C # 八个字符
年(可选,留空)(Year) 1970~2099 , - * / 四个字符

 

  注意事项:

  每一个域都使用数字,但还可以出现如下特殊字符,它们的含义是:

  (1):表示匹配该域的任意值。假如在Minutes域使用, 即表示每分钟都会触发事件。

  (2)?:只能用在DayofMonth和DayofWeek两个域。它也匹配域的任意值,但实际不会。因为DayofMonth和DayofWeek会相互影响。例如想在每月的20日触发调度,不管20日到底是星期几,则只能使用如下写法: 13 13 15 20 * ?, 其中最后一位只能用?,而不能使用,如果使用表示不管星期几都会触发,实际上并不是这样。

  (3)-:表示范围。例如在Minutes域使用5-20,表示从5分到20分钟每分钟触发一次

  (4)/:表示起始时间开始触发,然后每隔固定时间触发一次。例如在Minutes域使用5/20,则意味着5分钟触发一次,而25,45等分别触发一次.

  (5),:表示列出枚举值。例如:在Minutes域使用5,20,则意味着在5和20分每分钟触发一次。

  (6)L:表示最后,只能出现在DayofWeek和DayofMonth域。如果在DayofWeek域使用5L,意味着在最后的一个星期四触发。

  (7)W:表示有效工作日(周一到周五),只能出现在DayofMonth域,系统将在离指定日期的最近的有效工作日触发事件。例如:在 DayofMonth使用5W,如果5日是星期六,则将在最近的工作日:星期五,即4日触发。如果5日是星期天,则在6日(周一)触发;如果5日在星期一到星期五中的一天,则就在5日触发。另外一点,W的最近寻找不会跨过月份 。

  (8)LW:这两个字符可以连用,表示在某个月最后一个工作日,即最后一个星期五。

  (9)#:用于确定每个月第几个星期几,只能出现在DayofMonth域。例如在4#2,表示某月的第二个星期三。

  三、常用表达式例子

  (1)0 0 2 1 * ? * 表示在每月的1日的凌晨2点调整任务

  (2)0 15 10 ? * MON-FRI 表示周一到周五每天上午10:15执行作业

  (3)0 15 10 ? 6L 2002-2006 表示2002-2006年的每个月的最后一个星期五上午10:15执行作

  (4)0 0 10,14,16 * * ? 每天上午10点,下午2点,4点

  (5)0 0/30 9-17 * * ? 朝九晚五工作时间内每半小时

  (6)0 0 12 ? * WED 表示每个星期三中午12点

  (7)0 0 12 * * ? 每天中午12点触发

  (8)0 15 10 ? * * 每天上午10:15触发

  (9)0 15 10 * * ? 每天上午10:15触发

  (10)0 15 10 * * ? * 每天上午10:15触发

  (11)0 15 10 * * ? 2005 2005年的每天上午10:15触发

  (12)0 * 14 * * ? 在每天下午2点到下午2:59期间的每1分钟触发

  (13)0 0/5 14 * * ? 在每天下午2点到下午2:55期间的每5分钟触发

  (14)0 0/5 14,18 * * ? 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发

  (15)0 0-5 14 * * ? 在每天下午2点到下午2:05期间的每1分钟触发

  (16)0 10,44 14 ? 3 WED 每年三月的星期三的下午2:10和2:44触发

  (17)0 15 10 ? * MON-FRI 周一至周五的上午10:15触发

  (18)0 15 10 15 * ? 每月15日上午10:15触发

  (19)0 15 10 L * ? 每月最后一日的上午10:15触发

  (20)0 15 10 ? * 6L 每月的最后一个星期五上午10:15触发

  (21)0 15 10 ? * 6L 2002-2005 2002年至2005年的每月的最后一个星期五上午10:15触发

  (22)0 15 10 ? * 6#3 每月的第三个星期五上午10:15触发

可以使用在线生成工具

 

 

 

 

posted on 2020-10-14 16:37  你能回来吗  阅读(132)  评论(2)    收藏  举报

导航