likes
comments
collection
share

SpringBoot开发实用篇01

作者站长头像
站长
· 阅读数 18

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第9天,点击查看活动详情


⭐️前面的话⭐️

✉️坚持和努力一定能换来诗与远方! 💭推荐书籍:📚《王道408》,📚《深入理解 Java 虚拟机-周志明》,📚《Java 核心技术卷》 💬算法刷题:✅力扣🌐牛客网 🎈Github 🎈码云Gitee


1 热部署

  • 手动启动热部署
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
</dependency>
  • 自动启动热部署(设置自动构建项目)

SpringBoot开发实用篇01

SpringBoot开发实用篇01

  • 热部署范围配置
# 默认不触发重启的目录列表
/META-INF/maven
/META-INF/resources
/resources
/static
/public
/templates

# 自定义不参与重启排除项
spring:   
  devtools:
    restart:
      # 设置不参与热部署的文件或文件夹
      exclude: static/**,public/**,config/application.yml
  • 关闭热部署(设置高优先级属性禁用热部署)
System.setProperty("spring.devtools.restart.enabled","false");

2 配置高级

2.1 @ConfigurationProperties

@ConfigurationProperties可以为第三方bean绑定属性

@Bean
@ConfigurationProperties(prefix = "datasource")
public DruidDataSource datasource(){
    DruidDataSource ds = new DruidDataSource();
    //ds.setDriverClassName("com.mysql.jdbc.Driver123");
    return ds;
}
dataSource:
  driverClassName: com.mysql.jdbc.Driver789

@EnableConfigurationProperties注解可以将使用@ConfigurationProperties注解对应的类加入Spring容器管控

@SpringBootApplication
@EnableConfigurationProperties(ServerConfig.class)
public class Springboot13ConfigurationApplication {
}
@Data
//@Component
@ConfigurationProperties(prefix = "servers")
public class ServerConfig {
}

📢 注意:@EnableConfigurationProperties与@Component不能同时使用

解除使用@ConfigurationProperties注释警告

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
</dependency>

2.2 宽松绑定/松散绑定

@ConfigurationProperties绑定属性支持属性名宽松绑定

SpringBoot开发实用篇01

📢 注意:宽松绑定不支持注解@Value引用单个属性的方式

📢 注意:绑定前缀名命名规范:仅能使用纯小写字母、数字、下划线作为合法的字符

2.3 常用计量单位绑定

SpringBoot支持JDK8提供的时间与空间计量单位

@Data
@Component
@ConfigurationProperties(prefix = "servers")
public class ServerConfig {
    private String ipAddress;
    private int port;
    private long timeout;
    
    @DurationUnit(ChronoUnit.HOURS)
    private Duration serverTimeOut;
    @DataSizeUnit(DataUnit.MEGABYTES)
    private DataSize dataSize;
}

SpringBoot开发实用篇01

2.4 数据校验

开启数据校验有助于系统安全性,J2EE规范中JSR303规范定义了一组有关数据校验相关的API

①:添加 JSR303 规范坐标与 Hibernate 校验框架对应坐标

<!--1.导入JSR303规范-->
<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
</dependency>
<!--使用hibernate框架提供的校验器做实现类-->
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
</dependency>

②:对 Bean 开启校验功能

③:设置校验规则

@Data
@Component
@ConfigurationProperties(prefix = "servers")
//2.开启对当前bean的属性注入校验
@Validated
public class ServerConfig {
    //3.设置具体的规则
    @Max(value = 8888,message = "最大值不能超过8888")
    @Min(value = 202,message = "最小值不能低于202")
    private int port;
}

3 测试

  • 加载测试专用属性
  • 加载测试专用配置
  • Web环境模拟测试
  • 数据层测试回滚
  • 测试用例数据设定

4 数据层解决方案

  • 技术选型:Druid + MyBatis-Plus + MySQL
    • 数据源:Druid DataSource
    • 持久化技术:MyBatis-Plus / MyBatis
    • 数据库:MySQL

5 数据源配置

  • 2 种格式 SpringBoot开发实用篇01

  • SpringBoot提供了 3 种内嵌的数据源对象供开发者选择

    • HikariCP:默认内置数据源对象
    • Tomcat 提供DataSource:HikariCP不可用的情况下,且在web环境中,将使用 tomcat 服务器配置的数据源对象
    • Commons DBCP:Hikari不可用,tomcat数据源也不可用,将使用dbcp数据源

通用配置无法设置具体的数据源配置信息,仅提供基本的连接相关配置,如需配置,在下一级配置中设置具体设定

SpringBoot开发实用篇01

6 JdbcTemplate(SpringBoot内置持久化解决方案)

SpringBoot开发实用篇01

  • 添加依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
  • JdbcTemplate 配置
spring: 
  jdbc:  
    template:
      query-timeout: -1   # 查询超时时间
      max-rows: 500       # 最大行数
      fetch-size: -1      # 缓存行数

7 内嵌数据库 H2

  • SpringBoot提供了3种内嵌数据库供开发者选择,提高开发测试效率

    • H2
    • HSQL
    • Derby
  • 导入H2相关坐标

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
  • 设置当前项目为web工程,并配置H2管理控制台参数(访问用户名sa,默认密码123456)
server:
  port: 80
spring:
  h2:
	console:
	  path: /h2
	    enabled: true
  • 操作数据库(创建表)
create table tbl_book (id int,name varchar,type varchar,description varchar)
  • 设置访问数据源(SpringBoot可以根据url地址自动识别数据库种类,在保障驱动类存在的情况下,可以省略配置)
server:
  port: 80

spring:
  datasource:
    # 可省
    driver-class-name: org.h2.Driver
    url: jdbc:h2:~/test
    username: sa
    password: 123456
  h2:
    console:
      path: /h2
      enabled: true
  • H2数据库控制台仅用于开发阶段,线上项目请务必关闭控制台功能
server:
  port: 80
spring:
 	h2:
 	  console:
 	    path: /h2
 	    enabled: false

SpringBoot开发实用篇01

8 Redis(NoSQL解决方案1)

市面上常见的NoSQL解决方案:

  • Redis
  • Mongo
  • ES
  • Solr

8.1 概念、安装和启动

  • Redis是一款 key-value 存储结构的内存级NoSQL数据库

    • 支持多种数据存储格式
    • 支持持久化
    • 支持集群
  • Redis下载( Windows版):github.com/tporadowski…

  • Redis安装与启动( Windows版)

    • Windows解压安装或一键式安装
    • 服务端启动命令:redis-server.exe redis.windows.conf
    • 客户端启动命令:redis-cli.exe

SpringBoot开发实用篇01

SpringBoot开发实用篇01

8.2 与SpringBoot整合

  • 坐标
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  • 配置
spring:
  redis:
    host: localhost # 127.0.0.1
    port: 6379

SpringBoot开发实用篇01

  • RedisTemplate 提供操作各种数据存储类型的接口API

SpringBoot开发实用篇01

  • 🤔思考:cmd 命令窗口和 IDEA中 RedisTemplate 操作的是同一个 Redis 吗?
    • RedisTemplate<K, V>的 K,V 未指定都是 Object。客户端 RedisTemplate 以对象作为key和value,内部对数据进行序列化。
    • 客户端 StringRedisTemplate extends RedisTemplate<String, String> 以字符串作为 key 和 value,与 Redis客户端(cmd命令窗口)操作等效。

8.3 开发

  • ⭐开发使用

    • RedisTemplate
    • StringRedisTemplate(常用)
  • ⚡Redis client

# lettuce(内部默认实现)(连接是基于 Netty 的,Netty 是一个多线程、事件驱动的 I/O 框架。)
底层设计中采用 StatefulRedisConnection。
StatefulRedisConnection 自身是线程安全的,可以保障并发访问安全问题,所以一个连接可以被多线程复用。
当然lettcus也支持多连接实例一起工作。

# jedis(直接连接Redis Server即直连模式,如果在多线程环境下使用 jedis是非线程安全的。)
解决方案可以通过配置连接池使每个连接专用,这样整体性能就大受影响。
  • 选择 jedis

(1)依赖

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>

(2)配置

spring:
  redis:
    host: localhost
    port: 6379
    client-type: jedis
    lettuce:
      pool:
        max-active: 16
    jedis:
      pool:
        max-active: 16

9 Mongodb(NoSQL解决方案2)

MongoDB is an open-source NoSQL document database that uses a JSON-like schema instead of traditional table-based relational data.

参考:

9.1 应用场景

SpringBoot开发实用篇01

9.2 安装、启动、可视化

  • windows
# 下载 msi 文件、安装
https://www.mongodb.com/try/download/community
# 创建3个文件夹:/data、/log、/data/log/


# 服务端启动  C:\Program Files\MongoDB\Server\5.0\bin>
mongod --dbpath C:\enviroment\mongodb\data\db

# 客户端启动
mongo
# 配置环境变量
# 可视化客户端——Robo 3T、Navicat
  • macos
# 启动
in ~/environment/mongodb 
$ mongod --fork --dbpath data --logpath log/mongodb.log --logappend
$ mongo

# 可视化客户端
Robo 3T

# 退出
# 验证权限;赋予权限;关闭
> db.auth('root','123456')
1
> db.grantRolesToUser("root",[{role:"hostManager",db: "admin"}])
> use admin
switched to db admin
> db.shutdownServer({force:true})

SpringBoot开发实用篇01

SpringBoot开发实用篇01

# 基础操作CRUD
// 添加数据(文档)
// db.book.save({"name":"springboot基础篇",type:"springboot"})

// 删除操作
// db.book.remove({type:"springboot"})

// 修改操作
db.book.update({name:"springboot"},{$set:{name:"springboot2"}})

// 查询操作
// db.getCollection('book').find({})
db.book.find()

SpringBoot开发实用篇01

9.3 命令操作

  • 关于库、集合/表
显示所有数据库:show dbs / show databases(都是一样的)

显示所有集合/表:show collections / show tables(都是一样的)

显示当前使用数据库:db / db.getName();

数据库状态:db.stats()

9.4 与SpringBoot整合

  • 坐标
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
  • 配置
spring:
  data:
    mongodb:
      uri: mongodb://localhost/itheima
  • 测试
@SpringBootTest
class Springboot17MongodbApplicationTests {
    @Autowired
    private MongoTemplate mongoTemplate;

    @Test
    void contextLoads() {
        Book book = new Book();
        book.setId(2);
        book.setName("springboot2");
        book.setType("springboot2");
        book.setDescription("springboot2");

        mongoTemplate.save(book);
    }

    @Test
    void find(){
        List<Book> all = mongoTemplate.findAll(Book.class);
        System.out.println(all);
    }
}

10 ElasticSearch(ES)(NoSQL解决方案3)

参考:

🍓10.1 介绍 、应用场景

Elasticsearch 是一个基于 Lucene 的搜索服务器,它给我们提供了一个分布式的全文搜索引擎。Elasticsearch 是用 Java 语言开发、基于 Apache 协议的开源项目,它也是目前最受欢迎的企业搜索引擎。Elasticsearch 广泛运用于云计算中,能够达到实时搜索,具有稳定,可靠,快速的特点。

简单来说 Elasticsearch 是一个实时的分布式搜索分析引擎,它能让你以前所未有的速度和规模,去探索你的数据。

它被用作全文检索、结构化搜索、分析,现在也有很多知名企业或网站都在使用 Elasticsearch 来实现搜索服务,比如:

  • Wikipedia(维基百科)使用 Elasticsearch 提供带有高亮片段的全文搜索。
  • Stack Overflow(全球最大的技术问答网站)将地理位置查询融入全文检索中去,并且使用 more-like-this 接口去查找相关的问题和回答。
  • GitHub 使用 Elasticsearch 对上千亿行代码进行查询。
  • ...

SpringBoot开发实用篇01

🍓10.2 安装 Elasticsearch7.15.1

  • windows

  • macos

    • 下载 .tar.gz 压缩包
    • 解压
    • ./bin/elasticsearch 启动

🍓10.3 安装客户端 Kibana7.15.1

操作 Elasticsearch 前,我们需要准备一个客户端(就像用 Navicat 操作数据库一样)

  • Kibana 可以使大数据通俗易懂。它很简单,基于浏览器的界面便于您快速创建和分享动态数据仪表板来追踪 Elasticsearch 的实时数据变化。
  • 搭建 Kibana 非常简单。您可以分分钟完成 Kibana 的安装并开始探索 Elasticsearch 的索引数据 —— 没有代码、不需要额外的基础设施。
  • windows

  • macos

    • 下载、解压:www.elastic.co/cn/download…
    • 修改配置文件:./config/kibana.yml 修改es的地址,改为自己本机IP地址,如果是服务器的话,改为服务器地址就好了。
    • 启动:bin/kibana
    • 访问

🍓10.4 核心概念

关系型数据库非关系型数据库
数据库 Database索引 Index
表 Table索引 Index(原为 Type)
数据行 Row文档 Document
数据列 Column字段 Field
约束 Schema映射 Mapping

🌍索引 index

Index就相当于数据库中的数据表, ElasticSearch会索引所有字段,经过处理后写入一个反向索引(Inverted Index)。查找数据的时候,直接查找该索引。

所以,ElasticSearch数据管理的顶层单位就叫做 Index(索引)

访问 http://localhost:9200/_cat/indices?v ,如果页面只显示了一句“health status index uuid pri rep docs.count docs.deleted store.size pri.store.size”,则证明还未创建索引!

我们通过 kibana(http://127.0.0.1:5601/app/kibana#/dev_tools/) 来创建索引,如下

SpringBoot开发实用篇01

PUT /test?pretty 代表我们需要创建一个名为 test 的索引,pretty 参数则是要求它返回一个漂亮的 json 结果

查看索引信息:http://localhost:9200/_cat/indices?v

SpringBoot开发实用篇01

也可以通过 kibana 来查看索引信息👇

SpringBoot开发实用篇01

🌍节点(node)和集群(Cluster)

节点(node)是一个运行着的 Elasticsearch 实例,你可以认为是单个服务器。

集群(cluster)是一个或多个节点的集合,他们协同工作,共享数据并提供故障转移和扩展功能。一个集群对应着一个唯一的名字标识(默认是“ElasticSearch”),一个节点只能通过指定某个集群的名字,来加入这个集群

访问 http://localhost:9200/_cat/health?v 来查看当前集群的状态

SpringBoot开发实用篇01

集群的状态一共有三种值,分别是:

  • Green:  即最佳状态
  • Yellow:  即数据和集群可用,但是集群的备份有的是坏的
  • Red:  即数据和集群都不可用

可以通过访问 http://localhost:9200/_cat/nodes?v 来查看节点信息👇

SpringBoot开发实用篇01

🌍类型(TYPE)

在一个索引中,你可以定义一种或多种类型。一个类型对应着你的索引在一种逻辑上的分类,其语义完全由你来定。通常,会为具有一组共同字段的文档定义一个类型。

比如你运营着一个购物平台,并且你将所有的数据都存储到一个索引中,那么在这个索引中,你可以为用户数据定义一个类型,为商品数据定义另一个类型,也可以将订单数据定义成另一个类型。

这就有点像在 Mysql 中建表一样,将同样类型的数据都放在同一个表中。

🌍文档(document)

一个文档是一个可被索引的基础信息单元(文档以 JSON 格式来表示)

文档中存储的数据结构与对象一致,一个对象可以直接保存成一个文档。

{
    "id":"1",
    "name":"小菜",
    "department":{
        "id":"1",
        "deptName":"搬砖部",
        "describe":"努力搬好每一块砖"
    }
}

🌍字段(Field)

相当于是 Mysql 中某张表所拥有的若干个字段,它可以根据不同的属性对文档数据进行分类标识。

🌍接近实时(NRT)

Elasticsearch 是一个接近实时的搜索引擎。这意味着,从索引一个文档到这个文档能够被搜索到会有一个轻微的延迟(通常是1秒)。

🌍分片(shards)和复制(replicas)

🌍映射(mapping)

mapping 是对处理数据的方式和规则方面做出的一些限制,如某个字段的数据类型、默认值、是否被索引等等,这些都是映射里面做出限制。

🍓10.5 基础操作

📍创建索引、查询索引、删除索引

# 查看ElasticSearch信息
GET /
# 创建索引
PUT /employee
PUT /test1

# 删除索引
DELETE /test1

📍新建文档并建立索引

📍查询指定索引

# 在 employee 索引中演示基本的增删改查操作
# 1、创建员工信息
PUT /employee/_doc/1
{
    "id":"1",
    "name":"小菜",
    "department":{
        "id":"1",
        "deptName":"搬砖部",
        "describe":"努力搬好每一块砖"
    }
}

# 2、获取员工信息
GET /employee/_doc/1
# 3、批量查询 (_mget)
GET /employee/_mget
{
  "docs" : [
      {
         "_id" : 1
      },
      {
         "_id" : 2
      }
   ]
}

PUT /employee/_doc/2
{
    "id":"1",
    "name":"小z",
    "department":{
        "id":"1",
        "deptName":"搬砖部2",
        "describe":"努力搬好每一块砖"
    }
}

SpringBoot开发实用篇01

📍修改某个文档(全部修改)

📍修改某个文档(部分修改)

🍓10.6 与SpringBoot整合

整合步骤

  • 依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<!--SpringBoot平台并没有跟随ES的更新速度进行同步更新,ES提供了High Level Client操作ES-->
<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
</dependency>
  • 配置
spring:
  elasticsearch:
    rest:
      uris: http://localhost:9200
  • 客户端
@SpringBootTest
class Springboot18EsApplicationTests {
    @Autowired
    private ElasticsearchRestTemplate template;
}

执行查询

// 执行查询、还支持异步查询
@Test
public void getTest() throws Exception {
    // 1,构建请求
    GetRequest getRequest = new GetRequest("employee", "1");
    // 2,执行查询
    GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT);
    // 3,获取结果
    if (getResponse.isExists()){
        System.out.println(getResponse.getSourceAsString());//以String获取数据
    }

    //{"id":"1","name":"小菜","department":{"id":"1","deptName":"搬砖部","describe":"努力搬好每一块砖"}}
}

执行新增

//执行新增
@Test
public void insertTest() throws Exception {
    // 1,构建请求
    IndexRequest indexRequest = new IndexRequest("employee");
    indexRequest.id("8");
    Map<String, String> insertInfo = new HashMap<>();
    insertInfo.put("id", "8");
    insertInfo.put("name", "小张");
    indexRequest.source(JSON.toJSONString(insertInfo), XContentType.JSON);
    // 2,执行新增
    IndexResponse indexResponse = client.index(indexRequest, RequestOptions.DEFAULT);
    // 3,获取处理结果
    System.out.println("ID: " + indexResponse.getId() + ", RESULT: " + indexResponse.getResult());

    // ID: 8, RESULT: CREATED
}

新增的参数支持多种方式:

//方式1: json
Map<String, String> insertInfo = new HashMap<>();
insertInfo.put("id", "8");
insertInfo.put("name", "小张");
request.source(JSON.toJSONString(insertInfo), XContentType.JSON);

//方式2: map
Map<String, String> insertInfo = new HashMap<>();
insertInfo.put("id", "8");
insertInfo.put("name", "小张");
request.source(insertInfo);

//方式3: XContentBuilder
XContentBuilder builder = XContentFactory.jsonBuilder();
builder.startObject();
{
    builder.field("id", "8");
    builder.field("name", "小张");
}
builder.endObject();
request.source(builder);

//方式4: 直接构建
request.source("id", "8", "name", "小张");

执行修改

//执行修改
@Test
public void updateTest() throws Exception {
    // 1,构建请求
    UpdateRequest updateRequest = new UpdateRequest("employee", "8");
    //updateRequest.type("_doc");
    Map<String, String> updateInfo = new HashMap<>();
    updateInfo.put("name", "小张小李");
    updateRequest.doc(JSON.toJSONString(updateInfo), XContentType.JSON);

    //updateRequest.docAsUpsert(true);

    //// 设置超时时间
    //updateRequest.timeout("1s");
    //// 设置重试次数
    //updateRequest.retryOnConflict(3);

    // 2,执行修改
    UpdateResponse updateResponse = client.update(updateRequest, RequestOptions.DEFAULT);
    // 3,获取处理结果
    System.out.println("ID: " + updateResponse.getId() + ", RESULT: " + updateResponse.getResult());

}

执行删除

// 执行删除
@Test
public void deleteTest() throws Exception {
    // 1,构建请求
    DeleteRequest deleteRequest = new DeleteRequest("employee", "8");
    // 2,执行查询
    DeleteResponse deleteResponse = client.delete(deleteRequest, RequestOptions.DEFAULT);
    // 3,获取结果
    System.out.println("ID: " + deleteResponse.getId() + ", RESULT: " + deleteResponse.getResult());
    // ID: 8, RESULT: DELETED

    // {"id":"1","name":"小菜","department":{"id":"1","deptName":"搬砖部","describe":"努力搬好每一块砖"}}
    // {"name":"小张","id":"8"}
}

执行批量

@Test
public void bulkTest() throws Exception{
    // 1,构建请求
    BulkRequest bulkRequest = new BulkRequest();
    Map<String, String> insertInfo = new HashMap<>();
    insertInfo.put("id", "10");
    insertInfo.put("name", "小张明");

    System.out.println("--------------------------" + JSON.toJSONString(insertInfo));

    bulkRequest.add(new IndexRequest("post").id("900").source(JSON.toJSONString(insertInfo), XContentType.JSON));
    bulkRequest.add(new IndexRequest("employee").id("800").source(JSON.toJSONString(insertInfo), XContentType.JSON));
    
    // 2,批量执行
    BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT);
    // 3,获取结果
    for (BulkItemResponse bulkItemResponse : bulkResponse) {
        DocWriteResponse response = bulkItemResponse.getResponse();
        switch (bulkItemResponse.getOpType()){
            case INDEX:
                IndexResponse indexResponse = (IndexResponse) response;
                System.out.println("INDEX:" + indexResponse.getResult());
                break;
            case CREATE:
                IndexResponse createResponse = (IndexResponse) response;
                System.out.println("CREATE:" + createResponse.getResult());
                break;
            case UPDATE:
                UpdateResponse updateResponse = (UpdateResponse) response;
                System.out.println("UPDATE:" + updateResponse.getResult());
                break;
            case DELETE:
                DeleteResponse deleteResponse = (DeleteResponse) response;
                System.out.println("DELETE:" + deleteResponse.getResult());
                break;
        }
    }

    // INDEX:CREATED
    // INDEX:CREATED
}