Java – SpringBoot – 进阶整合实例

简介

本文主要讲解SpringBoot项目的系统部署方面的进阶教程,基础整合教程可阅读下面文章

简介 本文通过SpringBoot完整的整合实例,讲解如何使用SpringBoot从搭建到页面RESTful请求的全过程分析……
2023-01-29

本篇将主要讲解SpringBoot的热加载、缓存、整合第三方程序、监控等内容。

 

热部署、热启动

热部署指的是我们在修改完SpringBoot项目的java代码后,不需要重复重启项目,在项目还在启动的状态下热加载新修改的java代码。

热部署需要使用SpringBoot的热加载坐标

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

IDEA通过快捷键【Ctrl+F9】或点击【构建->构建项目】即可在不重启整个项目的情况下执加载

 

自动热部署

通过设置IDEA可以不参与手动构建项目的情况下,自动热加载项目

实现自加载后,IDEA会在失去焦点5秒后,自动重新加载

IDEA设置【文件->设置->构建、执行、部署->编译器->自动构建项目】

 

IDEA设置【文件->设置->高级设置->编译器->即使开发的应用程序当前正在运行,也允许自动 make 启动】

 

旧版本IDEA可以按快捷键【Ctrl+Alt+Shift+/】唤起维护

选择【compiler.automake.allow.when.app.running】为true

 

热部署范围

热部署由spring-boot-devtools包控制,因此devtools默认对以下目录不进行热部署

/META-INF/maven
/META-INF/resources
/resources/**
/static/**
/public/**
/templates/**

 

因是Boot Framework项目中的包,所以我们可以在配置文件中进行自定义排除那些文件夹不进行热加载处理

spring:
  devtools:
    restart:
      exclude: static/**,public/**

 

开启与关闭热部署

设置 enabled 属性即可开启或关闭,其优先级遵循配置文件优先级。

spring:
  devtools: 
    restart: 
      exclude: static/**,public/**
      enabled: false

 

 

SpringBoot 高级配置

第三方Bean属性注入

SpringBoot 项目中的配置项不单可以配置来自官方的Bean的配置,还可以对自定义的Bean类进行配置。

使用【@ConfigurationProperties】注解,当注解在类时,会对配置文件中的头部配置进行注入

yml配置文件中的自定义配置项:

customconfig: 
  server: "127.0.0.1"
  port: 1234
  address: "xxxxx"

 

类中使用【@ConfigurationProperties】注解自动注入配置

@Component
@ConfigurationProperties("customconfig")
public class ServerConfig {
    private String server;
    private Integer port;
    private String address;
}

类属性中和配置文件中的属性名字需要一致。

 

关于出现【未配置 Spring Boot 配置注解处理器】的问题

可以加入以下坐标

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

 

@EnableConfigurationProperties

【@EnableConfigurationProperties】和【@ConfigurationProperties】的区别在于,@EnableConfigurationProperties 定义那些类需要使用配置项注入,并把这个类作为Bean提交给Spring管理,而 @ConfigurationProperties 只定义使用那个配置项注入。

 

// 定义 ServerConfig 类作为 Bean,并且使用配置项注入
@EnableConfigurationProperties({ServerConfig.class})
public class Application { }

// 当 @EnableConfigurationProperties 定义了这个类后,这个类就已经是 受Spring 管理的 Bean了,此时不能添加 @Component 注解,否则Spring中就会出现两个 ServerConfig 的Bean
// @Component

// 当 @EnableConfigurationProperties 定义了这个类后,这个类可以使用
// @ConfigurationProperties("customconfig") 来指定注入那个配置项作为这个类的属性值
@ConfigurationProperties("customconfig")
public class ServerConfig {
    private String server;
    private Integer port;
    private String address;
}

 

 

配置的宽松绑定

SpringBoot 对Bean类注入配置项时,具有非常宽松的规定,以下面代码的配置为例

@Component
@ConfigurationProperties("customconfig")
public class ServerConfig {
    private String server;
    private Integer port;
    private String address;
}

yml 配置文件
customconfig: 
  server: "127.0.0.1" 

SpringBoot 对于配置文件的命名非常宽松,以下配置项的命名,都可以被匹配

 

customconfig: 
  server: "127.0.0.1" 
  Server: "127.0.0.1" 
  SERVER: "127.0.0.1" 
  Se-rver: "127.0.0.1" 
  S_e-r_v_e-r: "127.0.0.1" 
  S_E_R_V_E_R: "127.0.0.1" 

 

 

配置单位定义

在配置中,我们经常会配一些和数字有关的量,但是这些量通常情况下都是具有单位的,如下

customconfig: 
  timeout: 100

从上面的配置,我们只知道量是100,但并不知道单位是什么。

 

Java 8 提供了一种带单位的计量

时间单位计量 Duration

   // 默认单位为 ms 毫秒,定义单位为 Day
    @DurationUnit(ChronoUnit.DAYS)
    private Duration timeout;

其它单位可参照ChronoUnit中的其它选项

 

 

容量单位计量 DataSize

   // 默认单位为 B ,定义单位为 MB
    @DataSizeUnit(DataUnit.MEGABYTES)
    private DataSize size;

也可以直接在配置文件中定义单位

 

customconfig:
  size: 100MB

 

 

数据校验

对于配置文件中的配置项,存在数据类型不相符的情况,这样会导致系统加载配置时抛出异常,比如下面的代码

@ConfigurationProperties("customconfig")
public class ServerConfig {
    private Integer port;
}

yml 配置项
customconfig:
  # abc 并不能作为数字表示,引起异常抛出
  port: abc

 

 

引入数据校验

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

        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
            <version>2.0.1.Final</version>
        </dependency>

这一个坐标是对于JSR303规范的一个接口,还需要导入一个实现此接口的实现类

 

导入 Hibernate 实现包坐标

        <dependency>
            <groupId>org.hibernate.validator</groupId>
            <artifactId>hibernate-validator</artifactId>
        </dependency>

 

 

使用数据校验

1.对需要做数据校验的Bean类增加【@Validated】注解

@Validated
public class ServerConfig { }

2.对需要做数据校验的属性增加对应的注解

 

@Validated
public class ServerConfig {
    @Null(message = "server不能为空")
    private String server;

    @Max(value = 65535,message = "端口号不能大于65535")
    @Min(value = 1024,message = "端口号不能小于1024")
    private Integer port;

   @Email(message = "该字段只能使用Email格式")
    private String mail;
}

 

测试用例

测试专用属性值

对于专用于测试的用例,我们有时需要获得一些来自配置的属性值,有三种方法

方法一:直接写在yml配置项中

直接写在yml配置项中,在调用测试用例时获得yml配置值

test:
  prop: testValue

在测试类中获取配置项的值

 

@SpringBootTest
class ApplicationTests {
    @Value("${test.prop}")
    private String prop;
    @Test
    public void Test(){
        System.out.println(prop);
    }
}

但不推荐这样使用,原因是yml配置文件中的配置项建议用于项目正常运行时调用,如果测试的属性也在配置项中,容易出现混乱问题。

 

 

方法二:使用【@SpringBootTest】注解中的 properties 配置临时属性

测试用例注解【@SpringBootTest】中提供了一种属性 properties,可用于定义临时属性,这样可以不配置yml文件的情况下配置属性

@SpringBootTest(properties = {"test.prop=testValue1"})
class ApplicationTests {
    @Value("${test.prop}")
    private String prop;
    @Test
    public void Test(){
        System.out.println(prop);
    }
}

properties 接收一个字符串数组,可以接收多个属性值

 

 

方法三:使用【@SpringBootTest】注解中的 args 配置临时参数

在SpringBoot项目运行章节中提到,SpringBoot 可以在运行时提供临时运行参数值

java -jar springboot.jar --server.port=8080

在测试用例中,可以以源码级模拟运行时接收临时运行参数值

@SpringBootTest(args = {"--test.prop=testValue2"})
class ApplicationTests {
    @Value("${test.prop}")
    private String prop;
    @Test
    public void Test(){
        System.out.println(prop);
    }
}

注意:args 优先级最高,其次是 properties,优先最低是yml文件,相同配置会被覆盖。

 

测试专用Bean类

在测试案例中,我们可能需要临时用到某些第三方Bean类,如IPage分页功能上的过滤器,在项目开发中,IPage分页过滤器需要我们手动创建一个Bean并让IPage自动装配,但在测试用例中,如果我们也需要用到IPage分页功能时,我们可以创建一个专用于测试的临时Bean类

/**
 * 用于临时配置测试用例的Bean配置类
 */
@Configuration
public class MSGConfig {

    /**
     * 创建一个Bean,交给Spring管理,并自动装配
     * @return
     */
    @Bean
    public String msg(){
        return "Hello World";
    }
}

在测试用例中,就可以直接使用这个Bean

    @Autowired
    private String msg;
    @Test
    public void BeanTest(){
        System.out.println(msg);
    }

 

测试Controller表现层

针对DAO/Mapper层和Service层,我们都可以通过Test直接调用方法进行测试,但是表现层测试日常使用中只能通过浏览器或Postman等工具来测试

SpringBoot 提供了Mock模拟测试功能,在Test中我们可以开启模拟请求测试表现层。

1.通过【@AutoConfigureMockMvc】注册开启模拟测试功能

@SpringBootTest
@AutoConfigureMockMvc
public class TestWeb { }

 

2.开启测试用例的Web服务

测试用例默认不会开启Web服务,需要开启Web服务可以在【@SpringBootTest】中配置 webEnvironment 环境

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)

WebEnvironment.RANDOM_PORT -> 随机端口启动Web服务

WebEnvironment.NONE -> 不启动 Web 服务

WebEnvironment.DEFINED_PORT -> 按照默认配置启动或按照yml所配置的端口启动

 

3.自动装配MockMvc对象

    @Autowired
    MockMvc mvc;

 

4.通过调用 MockMvc 对象的 perform 方法提交模拟请求(MockMvc 对象可以直接在方法参数中装配)

    @Test
    public void test(@Autowired MockMvc mockMvc) throws Exception {
        RequestBuilder builder = MockMvcRequestBuilders.get("/books");
        mockMvc.perform(builder);
    }

RequestBuilder 是 perform 方法要求的参数,MockMvcRequestBuilders 提供GET/POST等各种请求方式。

 

测试模拟请求匹配状态

通过模拟请求后,需要对请求状态进行匹配

MockMvcResultMatchers 用于定义一个响应信息的对比类,它用于创建一个标准的响应信息对象。

通俗的讲,MockMvcResultMatchers 里有包含 cookies , status 等标准数据,创建MockMvcResultMatchers 对象后,再与当前请求后的结果进行对比

public void test(@Autowired MockMvc mockMvc){

    // 发出请求
    RequestBuilder builder = MockMvcRequestBuilders.get("/books");
    ResultActions perform = mockMvc.perform(builder);

    // 获取一个 Status 状态的标准模型
    StatusResultMatchers status = MockMvcResultMatchers.status();

    // 定义一个OK的状态,指的是 status 状态码是 200 的模型
    ResultMatcher ok = status.isOk();

    // 与请求获得的响应进行状态对比
    perform.andExpect(ok);

}

当然除了 状态码 200 外,也可以创建其它标准模型,来对请求后的响应进行对比。

 

测试模拟请求匹配响应体

与测试模拟请求匹配状态差不多

    @Test
    public void test(@Autowired MockMvc mockMvc) throws Exception {
        RequestBuilder builder = MockMvcRequestBuilders.get("/books");
        ResultActions perform = mockMvc.perform(builder);

        // 对请求状态进行匹配
        ContentResultMatchers content = MockMvcResultMatchers.content();
        
        ResultMatcher body = content.string("Hello");
        perform.andExpect(body);
    }

 

测试模拟请求匹配响应Json

    @Test
    public void test(@Autowired MockMvc mockMvc) throws Exception {
        RequestBuilder builder = MockMvcRequestBuilders.get("/books");
        ResultActions perform = mockMvc.perform(builder);
        
        ContentResultMatchers content = MockMvcResultMatchers.content();

        ResultMatcher json = content.json("{}");
        perform.andExpect(json);
    }

 

测试模拟请求匹配响应请求头

    @Test
    public void test(@Autowired MockMvc mockMvc) throws Exception {
        RequestBuilder builder = MockMvcRequestBuilders.get("/books");
        ResultActions perform = mockMvc.perform(builder);

        HeaderResultMatchers header = MockMvcResultMatchers.header();

        ResultMatcher body = header.string("Content-Type","application/json");
        perform.andExpect(body);
    }

 

测试数据回滚

在进行项目打包时,Maven工程会默认把Test测试用例中的所有方法都运行一遍,全部通过后才会进行打包

如果测试用例中包含数据库写操作的方法,那么打包完成后,数据库会莫名增加了一些数据

这是因为测试用例在运行时,如果包含写数据库操作的测试用例,那么测试完成后,测试用例中的数据会写到数据库中

可以通过增加【@Transactional】和【@Rollback(true)】注解开启事务管理,当数据库写入后没有commit()提交事务时,会自动回滚。

@Rollback 是一个开启事务的开关,默认开启,如果想关闭,传入false即可。

 

测试数据生成

测试中我们可能需要一些测试数据,我们可以自己创建,但是也可以使用SpringBoot提供给我们的假数据生成功能

在yml中,提供一种数据随机的配置功能。

testdata:
  book:
    id: ${random.int} # 生成随机数
    id2: ${random.int(1,100)} # 生成1~100之间的随机数
    uuid: ${random.uuid} # 生成 UUID
    value: ${random.value} # 生成字符串,会生成一个md5值
    long: ${random.long} # 生成一个长整型

 

数据层解决方案(SQL相关)

内置多数据源(Hikari)

在日常项目中,我们通常会导入Druid 数据源作为JDBC的数据源,对Mysql进行读写操作和管理,除了Druid 数据源以外,SpringBoot 提供了内置3种数据源,当不使用Druid数据源时,SpringBoot 会默认使用这3个数据源。

Hikari 数据源 : Hikari 数据源是轻量级数据源中速度最快的数据源实现包,因此SpringBoot 把Hikari 数据源内置并作为默认的省缺数据源。

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/ssmpDB?serverTimezone=UTC
    username: root
    password: root

 在不明确设置指定数据源或没有引入其它数据源的前提下时(如果引入了Druid数据源,即使不指定数据源,也默认加载Druid数据源),SpringBoot会默认使用Hikari数据源

或者以下配置,可以明确指定Hikari数据源

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/ssmpDB?serverTimezone=UTC
    hikari:
      driver-class-name: com.mysql.cj.jdbc.Driver
      username: root
      password: root

 

内置多数据源(Tomcat)

Tomcat 容器本身也包含了数据源实现类,但要启用它,需要SpringBoot启动Web环境下才可以

spring:
  datasource:
    tomcat:
      url: jdbc:mysql://127.0.0.1:3306/ssmpDB?serverTimezone=UTC
      driver-class-name: com.mysql.cj.jdbc.Driver
      username: root
      password: root

 

内置多数据源(Common DBCH)

spring:
  datasource:
    dbcp2:
      url: jdbc:mysql://127.0.0.1:3306/ssmpDB?serverTimezone=UTC
      driver-class-name: com.mysql.cj.jdbc.Driver
      username: root
      password: root

 

内置数据持久化框架

目常我们使用的数据持久化框架以Mybatis和Mybatis-Plus为主,其实SpringBoot内部也内置了一个数据持久化框架 JdbcTemplate

要使用内置的数据持久化框架JdbcTemplate,SpringBoot 默认是没有导入依赖的,需要先导入依赖

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

 

可以直接通过自动装配取得JdbcTemplate对象

@Autowired
    private JdbcTemplate jdbcTemplate;
    public void JdbcTemplateTest(){
        String sql = "select * from table;";


        RowMapper<Book> rm = new RowMapper<>() {
            @Override
            public Book mapRow(ResultSet rs, int rowNum) throws SQLException {
                Book temp = new Book();
                temp.setId(rs.getInt("id"));
                temp.setName(rs.getString("name"));
                temp.setType(rs.getString("type"));
                temp.setDescription(rs.getString("description"));
                return temp;
            }
        };
        jdbcTemplate.query(sql, rm);
    }

 

内置数据库

SpringBoot 内置了三款轻量级的数据库

H2 : H2 是一个内存级别的轻量级数据库,适合测试阶段使用,不适合上线阶段,因为是内存级别的数据库,因此其运行速度相到较快

SpringBoot 中配置H2数据库,需要导入依赖坐标

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

在配置中配置H2数据库,即可使用,同时H2数据库提供Web管理端,默认请求页面为“/h2-console”,可以自定义管理端页面地址

spring:
  datasource:
    druid:
      url: jdbc:h2:~test # 设置数据源的连接地址
      username: sa # 默认账号
      password: 123456 # 默认密码
      driver-class-name: org.h2.Driver # 驱动
  h2:
    console:
      enabled: true # 开启H2数据库
      path: "/h2"

HSQL : 略

Derby : 略

 

数据库技术选型

 

数据层解决方案(NoSQL相关)

SpringBoot整合Redis

Redis是一个Key-Value的NoSQL数据库。本节章只讲述如何把Reids整合到SpringBoot项目中。

1.添加SpringBoot专用的Starter坐标

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

 

2.配置文件中配置Redis配置信息

spring:
  redis:
    host: localhost
    port: 6379
    username:
    password: 

 

3.Redis提供RedisTemplate操作对象,使用它读写Redis数据库

    @Autowired
    private RedisTemplate redisTemplate;
    public void redisSetTest(){
        // 1.确定Redis存储的数据类型
        ValueOperations ops = redisTemplate.opsForValue();
        // 2.设置key和value数据
        ops.set("name","张三");
    }
    
    public void redisGetTest(){
        // 1.确定Redis存储的数据类型
        ValueOperations ops = redisTemplate.opsForValue();
        // 2.设置key和value数据
        ops.get("name");
    }

注意:使用RedisTemplate读写的Redis数据,将会被序列化后再存入Redis数据库中,和直接存储有区别

这是因为,RedisTemplate 对数据进行对象序列。

使用 StringRedisTemplate 对象进行读写纯String类型的数据

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    public void redisGetStringTest(){
        ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
        String name = ops.get("name");
        System.out.println(name);
    }

 

SpringBoot整合MongoDB

MongoDB 是一个开源、高性能、无模式的文档型数据库。NoSQL数据库产品中的一种,是最像关系型数据库的非关系型数据库。

本章节只讲述如何把MongoDB整合到SpringBoot中。

 

1.添加坐标

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

 

2.配置文件中配置MongoDB参数

spring:
  data:
    mongodb:
      uri: mongodb://localhost/unsoft # 连接数据库地址
      username: unsoft  # 用户名
      password: unsoft  # 密码
      database: unsoft  # 数据库名
      host: localhost   # 服务器地址

 

3.通过自动装配MongoTemplate对象进行CRUD操作

    @Autowired
    private MongoTemplate mongoTemplate;
    
    @Test
    public void mongodbTest(){
        Book book = new Book();
        book.setName("张三");
        book.setAddress("广东");
        book.setSex("男");
        mongoTemplate.insert(book);

        List<Book> all = mongoTemplate.findAll(Book.class);
        System.out.println(all);
    }

 

SpringBoot整合Elasticsearch(ES)

Elasticsearch是一个分布式全文搜索引擎。本章节只讲述SpringBoot整合Elasticsearch技术。

旧版本的Elasticsearch整合

1.导入Elasticsearch坐标依赖

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

 

2.配置Elasticsearch

spring:
  elasticsearch:
    uris: http://localhost:9200

 

3.通过自动装配Elasticsearch客户端对象

    @Autowired
    private ElasticsearchRestTemplate template;
    @Test
    public void ElasticsearchTest(){
        
    }

 

新版本的Elasticsearch整合

SpringBoot平台并没有跟随ES的更新速度进行同步更新,ES提供了High Level Client操作ES

1.导入坐标

        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-high-level-client</artifactId>
        </dependency>

2.因为高版本的没有整合到SpringBoot,因此不需要配置,且需要手动创建ES客户端

    @Test
    public void ESHighLevelTest() throws IOException {
        HttpHost host = HttpHost.create("http://localhost:9200");
        RestClientBuilder builder = RestClient.builder(host);
        RestHighLevelClient client = new RestHighLevelClient(builder);
        // 客户端操作,创建一个索引
        CreateIndexRequest request = new CreateIndexRequest("books");
        client.indices().create(request, RequestOptions.DEFAULT);
        
        client.close();
    }

 

SpringBoot整合第三方缓存技术

缓存Cache

缓存是一种介于数据永久存储介质与数据应用之间的数据临时存储介质

使用缓存可以有效的减少低速数据读取过程的次数(例如磁盘IO),提高系统性能。

缓存不仅可以用于提高永久性存储介质的数据读取效率,还可以提供临时的数据存储空间。

SpringBoot提供了缓存技术,方便缓存使用。

1.导入SpringBoot预设缓存技术坐标

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

 

2.启用缓存技术

通过在入口类中加入【@EnableCaching】注解,即可开启缓存技术

@EnableCaching
public class Application { }

 

3.在需要用到缓存的请求方法中,加入【@Cacheable】注解即使请求增加缓存

    @GetMapping
    @Cacheable(value = "cacheSpace",key = "#id")
    public String getByid(Integer id){
        return "Hello";
    }

value: 要把请求缓存放到那个容器中,名字自定义

key: 缓存技术会把每次新请求存放到空间中,但需要一个唯一的标识符,用于识别这个请求,可以使用 #prop 获得形参,通常使用形参作为这个请求的标识符,当下次请求时,缓存技术发现缓存中存在相同的标识符时,会直接从缓存中取出数据。

 

*4.如果在某些情况下只希望往缓存中存入数据,而非取出数据(如短信验证码生成,同一手机每一次请求都应该生成新的验证码)

可以使用【@CachePut】注解定义

    @CachePut(value = "cacheSpace",key = "#id")
    public String getByid(Integer id){
        System.out.println("Controller is Running...");
        return "Hello";
    }

 

 

其它缓存技术整合

SpringBoot除了可以使用自身预留的缓存技术以外,还支持以下第三方缓存技术

且SpringBoot提供了统一接口,更换缓存技术不需要过多的修改代码。

Generic

JCache

Ehcache

Hazelcast

Infinispan

Couchbase

Redis

Caffenine

Simple(SpringBoot预留)

memcached

Jetcache(阿里开发的缓存技术)

 

Ehcache

Ehcache 是一款第三方的cache缓存技术软件,SpringBoot中已对其进行统一整合,因此缓存技术更改为Ehcache无需更改代码,只需要配置Ehcache即可

1.导入Ehcache依赖坐标

        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache</artifactId>
        </dependency>

 

2.对Ehcache缓存技术进行配置

spring:
  cache:
    type: ehcache
    ehcache:
      config: ehcache.xml

 

3.创建一个对Ehcache的配置文件

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false">
    <!--默认缓存策略 -->
    <!-- external:是否永久存在,设置为true则不会被清除,此时与timeout冲突,通常设置为false-->
    <!-- diskPersistent:是否启用磁盘持久化-->
    <!-- maxElementsInMemory:最大缓存数量-->
    <!-- overflowToDisk:超过最大缓存数量是否持久化到磁盘-->
    <!-- timeToIdleSeconds:最大不活动间隔,设置过长缓存容易溢出,设置过短无效果-->
    <!-- timeToLiveSeconds:最大存活时间-->
    <!-- memoryStoreEvictionPolicy:缓存清除策略-->
    <defaultCache
            eternal="false"
            diskPersistent="false"
            maxElementsInMemory="1000"
            overflowToDisk="false"
            timeToIdleSeconds="60"
            timeToLiveSeconds="60"
            memoryStoreEvictionPolicy="LRU"/>

    <!-- 创建单独的Cache容器 -->
    <cache
            name="cacheName"
            eternal="false"
            diskPersistent="false"
            maxElementsInMemory="1000"
            overflowToDisk="false"
            timeToIdleSeconds="60"
            timeToLiveSeconds="60"
            memoryStoreEvictionPolicy="LRU"
    />

</ehcache>

 

Redis

Redis除了可以作为一个NoSQL数据库使用,因为它的key-value属性,也可以当作cache缓存使用,而且SpringBoot已经对Redis做了深层的整合。

1.导入Redis依赖坐标

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

 

2.配置文件中配置cache为Redis,并配置Redis服务器信息

spring:
  cache:
    type: redis
  redis:
    host: localhost
    port: 6379

 

3.关于cache一些属性的说明

spring:
  cache:
    type: redis
    redis:
      use-key-prefix: true     # 使用前缀,redis存储缓存时key使用的是 cacheName::key 的格式组合,如果为false,则不会添加cacheName
      time-to-live: 60s        # cache的有效时间
      key-prefix: cache        # key的前缀,如果use-key-prefix为false则无效,如果为true,则是 prefix+cacheName::key
      cache-null-values: false # 存储空值,一般不存储
  redis:
    host: localhost
    port: 6379

 

memcached

SpringBoot未提供对memcached的整合,需要使用硬编码方式实现客户端初始化管理。

1.添加xmemcached依赖项,因为memcached是一种缓存服务器,有三种连接客户端,分别是

Memcached Client for Java : 最早期客户端,稳定可靠,用户群广

SpyMemcached : 效率更高

Xmemcached : 并发处理更好

本文中使用Xmemcached作为客户端

        <dependency>
            <groupId>com.googlecode.xmemcached</groupId>
            <artifactId>xmemcached</artifactId>
            <version>2.4.7</version>
        </dependency>

 

 

2.手动创建xmemcached的Bean对象,因SpringBoot中没有整合Memcache,所以不需在配置文件中配置

@Component
public class XMemcachedConfig {

    @Bean
    public MemcachedClient getMemcacheClient() throws IOException {
        MemcachedClientBuilder mcb = new XMemcachedClientBuilder("localhost:11211");
        MemcachedClient client = mcb.build();
        return client;
    }
}

 

3.在业务层中对需要缓存的方法添加缓存数据

    @Autowired
    private MemcachedClient memcachedClient;

    @Autowired
    private Service service;

    @GetMapping
    public String getById(Integer id) {
        String result = service.get(id);
        memcachedClient.set(id.toString(), 10, result);
        return result;
    }

 

4.在需要取出数据时,使用get方法

memcachedClient.get(id.toString());

 

5.针对Memcache的配置问题,如果希望把配置写在yml配置文件上,则可以使用【自定义配置@ConfigurationProperties】手工加载

 

Jetcache

jetcache对Spring-cache进行了封装,在原有功能基础上实现了多级缓存、缓存统计、自动刷新、异步调用、数据报表等功能。

Jetcache设定了本地缓存与远程缓存的多级缓存解决方安

本地缓存(local)

LinkedHashMap

Caffeine

远程缓存(remote)

Redis

Tair

 

远程缓存配置

本节中使用Redis远程缓存技术

1.导入Jetcache start Redis坐标

<dependency>
    <groupId>com.alicp.jetcache</groupId>
    <artifactId>jetcache-starter-redis</artifactId>
    <version>2.7.3</version>
</dependency>

 

 

2.在配置文件中配置Jetcache使用何种方式连接缓存技术(本节使用远程连接Redis)

jetcache:
  remote:
    default:
      type: redis
      host: localhost
      port: 6379
      poolConfig:
        maxTotal: 50
    cache2: # 配置多个域,在创建缓存中的 area
      type: redis
      host: localhost
      port: 6379
      poolConfig:
        maxTotal: 50

 

3.在入口类中使用【@EnableCreateCacheAnnotation】注解,开启jetcache缓存技术

@EnableCreateCacheAnnotation
public class Application { }

 

 

4.在需要使用缓存技术的Service层,创建一个缓存容器

@Service
public class MsgServiceImpl implements MsgService {
    @CreateCache(area = "default", name = "cacheName::", expire = 60, timeUnit = TimeUnit.SECONDS)
    private Cache<String, String> cache;

    @Override
    public String getId(String id) {
        
        cache.put(id,xxxx);
        return null;
    }
}

area : 针对使用不同的缓存技术的域,Jetcache支持远程和本地缓存,在配置文件中的 default 就是 area 中定义的标识,将使用对应的area域,默认为 default,会和key合并成一个缓存记录的key

name : 缓存容器名称,因为会和key合并成一个缓存记录的key,所以建议name加上分隔符

expire : 过期时间

timeUnit : 过期时间的单位

 

本地缓存配置

本节中使用linkedHashMap本地缓存技术

1.导入Jetcache start Redis坐标

<dependency>
    <groupId>com.alicp.jetcache</groupId>
    <artifactId>jetcache-starter-redis</artifactId>
    <version>2.7.3</version>
</dependency>

 

2.在配置文件中配置Jetcache使用何种方式连接缓存技术(本节使用远程连接linkedHashMap)

 

jetcache:
  local:
    default: 
      type: linkedHashMap
      keyConvertor: fastjson  # 通常来说我们的key都是使用String作为标识,但如果使用对象作为key时,应将对象转为String,此处是指把对象转为json文本

 

3.在入口类中使用【@EnableCreateCacheAnnotation】注解,开启jetcache缓存技术

@EnableCreateCacheAnnotation
public class Application { }

 

4.在需要使用缓存技术的Service层,创建一个缓存容器

@Service
public class MsgServiceImpl implements MsgService {
    @CreateCache(area = "default", name = "cacheName::", expire = 60, timeUnit = TimeUnit.SECONDS, cacheType = CacheType.LOCAL)
    private Cache<String, String> cache;

    @Override
    public String getId(String id) {
        
        cache.put(id,xxxx);
        return null;
    }
}

area : 针对使用不同的缓存技术的域,Jetcache支持远程和本地缓存,在配置文件中的 default 就是 area 中定义的标识,将使用对应的area域,默认为 default,会和key合并成一个缓存记录的key

name : 缓存容器名称,因为会和key合并成一个缓存记录的key,所以建议name加上分隔符

expire : 过期时间

timeUnit : 过期时间的单位

cacheType : 要使用的缓存方式,默认为 远程,可以选择本地,远程,和两样同时用

 

Jetcache 配置

属性 默认值 说明
  jetcache.statIntervalMinutes 0 统计间隔,0表示不统计
  jetcache.hiddenPackages 自动生成name时,隐藏指定的包名前缀
  jetcache.[local|remote].${area}.type 缓存类型,本地支持linkedhashmap、caffeine,远程支持redis、tair
  jetcache.[local|remote].${area}.keyConvertor key转换器,当前仅支持fastjson
  jetcache.[local|remote].${area}.valueEncoder java 仅remote类型的缓存需要指定,可选java和kryo
  jetcache.[local|remote].${area}.valueDecoder java 仅remote类型的缓存需要指定,可选java和kryo
  jetcache.[local|remote].${area}.limit 100 仅local类型的缓存需要指定,缓存实例最大元素数
  jetcache.[local|remote].${area}.expireAfterWriteInMillis 无穷大 默认过期时间,毫秒单位
  jetcache.local.${area}.expireAfterAccessInMillis 0 仅local类型的缓存有效,毫秒单位,最大不活动间隔

 

Jetcache 注解的方法使用缓存

1.在启动类中增加【@EnableMethodCache(basePackages = "你要使用缓存的包名")】注解来开启并指定那个包使用注解方式来使用缓存技术。

注意:必须配合【@EnableCreateCacheAnnotation】使用

@EnableCreateCacheAnnotation
@EnableMethodCache(basePackages = "cn.unsoft.services")
public class Application { }

 

2.在Service层中的方法中添加【@Cached(name = "cacheName_", key = "#id", expire = 3600)】注解,定义此方法的缓存

@Service
public class BookServiceImpl implements BookService {
    @Autowired
    private BookDao bookDao;

    @Cached(name = "smsCache_", key = "#id", expire = 3600)
    @CacheRefresh(refresh = 10, timeUnit = TimeUnit.SECONDS)
    public Book getById(Integer id) {
        return bookDao.selectById(id);
    }
}

@Cached 注解是用于操作写入写出缓存的

name : 要操作的缓存容器

key : 缓存记录的标识

expire : 缓存过期时间,默认为秒

 

3.针对数据库出现修改时,但缓存数据并未同步修改的情况,Jetcache提供了删除和修改缓存的操作

@Service
public class BookServiceImpl implements BookService {
    // 修改缓存,当数据出现修改时,应把缓存数据也同步修改
    @CacheUpdate(name = "smsCache_", key = "#book.id", value = "#book")
    public boolean update(Book book) {
        return bookDao.updateById(book) > 0;
    }

    // 删除缓存,当数据被删除时,应把缓存数据也同步删除
    @CacheInvalidate(name = "smsCache_", key = "#id")
    public boolean delete(Integer id) {
        return bookDao.deleteById(id) > 0;
    }
}

 

4.关于缓存对象的问题

Redis 本身并不支持把Java对象存储到Redis表中,这会导致缓存Java对象时抛出异常。

解决方法:通过把对象转为序列化数据存储,当取出数据时,把序列化数据再转回对象

jetcache:
  remote:
    default:
      type: redis
      host: localhost
      port: 6379
      keyConvertor: fastjson
      valueEncoder: java
      valueDecoder: java

      poolConfig:
        maxTotal: 50

同时对象本身设置为可序列化对象

@Data
public class Book implements Serializable { }

 

5.开启缓存调用统计输出

jetcache:
  statIntervalMinutes: 15 # 每15分钟输出一次缓存触发统计报表到控制台。

 

 

SpringBoot整合第三方任务系统

定时任务是企业级应用中的常见操作

相关概念:

工作(Job):用于定义具体执行的工作

工作明细(JobDetail): 用于描述定时工作相关的信息

触发器(Trigger): 用于描述触发工作的规则,通常使用cron表达式定义调度规则

调度器(Scheduler):描述了工作明细与触发器的对应关系

 

Quartz 定时任务框架

Quartz 是一个能帮助Java程序处理定时任务的框架,它的任务细分有如下4点

1.创建一个具体的任务

2.增加这个任务的明细信息

3.创建一个触发器

4.创建一个调度器

具体实现:

1.导入Quartz 依赖坐标

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

 

 

2.创建一个具体的任务类,该类只提供给Quartz 去调用,所以无需提交给Spring管理,而是继承Quartz 的【QuartzJobBean】抽象类,并实现方法,该方法就是定义定时任务的具体任务逻辑

public class MyQuartz extends QuartzJobBean {
    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        context.getJobDetail();
        context.getTrigger();
        System.out.println("定时任务的具体实现逻辑");
    }
}

 

3.创建一个Quartz 配置Bean类,提交给Spring管理,配置Bean类用于定义【2】中定义的任务的触发和调度

@Configuration
public class QuartzConfig {

    /**
     * 工作明细,绑定具体的工作,相当于在这里创建一个任务清单
     * @return
     * toreDurably() 用于表示当任务不运行时进行持久化操作
     */
    @Bean
    public JobDetail jobDetail(){
        // 创建一个工作明细,明细中指定该工作是MyQuartz.class中定义的具体工作实现
        return JobBuilder.newJob(MyQuartz.class).storeDurably().build();
    }

    /**
     * 创建任务的调度和时间
     * CronScheduleBuilder.cronSchedule("秒 分 时 日 月 星期")
     * 【*】:表示任意时间
     * 【?】:表示通过其它时间进行推断
     * 【1~9】:表示具体时间
     * 【,】: 指定多个时间
     * 【/】: 间隔时间
     * 下面是举例:
     * 【0 0 0 1 * ?】 : 表示 【0 秒 0 分 0 时 1 日 任意月 对应星期】 会执行这一任务,因为 每月1号,与星期产生冲突,所以星期应根据日期自动推断
     * 【0,15,20,30 0 0 1 * ?】 : 表示会在每个月的 1号 0 时 0 分的 0秒、15秒、20秒、30秒 都执行一次任务
     * 【0/15 0 0 1 * ?】 : 表示会在每个月的 1号 0 时 0 分的 0秒 开始,每间隔15秒执行一次任务
     * 
     * 【W】: 只能用在日期中,表示当月中最接近某天的工作日,如【0 0 0 31W * ?】表示最接近31号的工作日,如果31号是星期六,则表示30号,即星期五,
     *        如果31号是星期天,则表示29号,即星期五。如果31号是星期三,则表示31号本身,即星期三。
     * 【L】: 表示最后(Last),只能用在日期和星期中,在日期中表示每月最后一天,在一月份中表示31号,在六月份中表示30号,
     *        也可以表示每月倒是第N天。例如: L-2表示每个月的倒数第2天
     *        【0 0 0 LW * ?】 LW可以连起来用,表示每月最后一个工作日,即每月最后一个星期五
     *        在星期中表示7即星期六
     *        0 0 0 ? * L
     *        表示每个星期六
     *        0 0 0 ? * 6L
     *        若前面有其他值的话,则表示最后一个星期几,即每月的最后一个星期五
     *  【#】: 只能用在星期中,表示第几个星期几,【0 0 0 ? * 6#3】表示每个月的第三个星期五。
     * @return
     */
    @Bean
    public Trigger trigger(){
        /**
         * 创建一个任务时间安排
         */
        ScheduleBuilder schedBuild = CronScheduleBuilder.cronSchedule("0 * * * * ?");

        /**
         * 创建一个调度,将在任务时间安排下处理对应的任务
         */
        return TriggerBuilder.newTrigger().withSchedule(schedBuild).forJob(jobDetail()).build();
    }

}

 

Spring Task 定时任务框架

Spring Task 定时任务比【Quartz】简单得多,只需要开启定时任务和在指定任务上加上注解并定义任务时间即可。

1.在启动类中定义开启定时任务注解【@EnableScheduling】

@SpringBootApplication
@EnableScheduling
public class Application { }

 

 

2.创建一个用于定义具体任务逻辑的Bean类,并在具体任务方法中定义任务执行时机

@Component
public class MySpringTask {

    /**
     * 在 每个月的 1 号的 0 时 0 分 0 秒开始,每隔15秒执行一次
     */
    @Scheduled(cron = "0/15 0 0 1 * ?")
    public void SpringTask(){
        System.out.println("具体任务逻辑");
    }
}

 

3.Sprint Task 部分具体配置

spring:
  task:
    scheduling:
    # 任务调度线程池大小 默认 1
      pool:
        size: 1
        # 调度线程名称前缀 默认 scheduling-
      thread-name-prefix: ssm_
      shutdown:
        # 线程池关闭时等待所有任务完成
        await-termination: false
        # 调度线程关闭前最大等待时间,确保最后一定关闭
        await-termination-period: 10s

 

定时任务时间表达式

可以到以下地址生成 cron 表达式

https://www.bejson.com/othertools/cron/

 

程序启动执行任务

Spring 提供一种方式允许我们在启动SpringBoot时候,即刻执行某些任务。

使用 CommandLineRunner 接口实责,通过实现 CommandLineRunner 的 run 方法,即可在程序启动之后第一时间执行,通过这种方法,可以做到程序启动时同步某些数据,比如把必要的数据同步到 Redis 中。

/**
 * 开启程序自动执行任务方法类
 */
@Component
public class TaskRunning implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("程序启动任务执行。");
    }
}

 

 

SpringBoot整合邮件JavaMail

JavaMail 在SpringBoot中的已包含整合,具体使用操作如下:

1.导入JavaMail依赖坐标

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

 

 

2.配置文件中加入 JavaMail 相关配置

spring:
  mail:
    host: 对应邮箱服务器地址
    username: 邮箱地址
    password: 邮箱密码

 

 

3.自动装配 JavaMail , 使用【SimpleMailMessage】封装邮件内容,使用【JavaMailSender】对象发送即可。


    @Autowired
    private JavaMailSender javaMailSender;

    private String from = "发送人邮箱地址(发送人别名)";
    private String to = "收件人邮箱地址";
    private String subTitle = "邮件标题";
    private String context = "邮件内容正文";

    @Test
    void javaMailTest() {

        SimpleMailMessage smp = new SimpleMailMessage();
        smp.setFrom(from);
        smp.setTo(to);
        smp.setSubject(subTitle);
        smp.setText(context);

        javaMailSender.send(smp);

    }

 

 

JavaMail 进阶

如果想利用JavaMail进行发送HTML类型的邮件,或者要在邮件中增加附件,可以通过【javaMailSender.createMimeMessage】创建富文本邮件

使用【MimeMessageHelper】为富文本邮件对象添加更多邮件功能。


    @Autowired
    private JavaMailSender javaMailSender;

    private String from = "发送人邮箱地址(发送人别名)";
    private String to = "收件人邮箱地址";
    private String subTitle = "邮件标题";
    private String context = "邮件内容正文";

    @Test
    void javaMailTest() {

        // 从 javaMailSender 中创建一个 MimeMail 富文本Mail对象
        MimeMessage mail = javaMailSender.createMimeMessage();

        try {
            /**
             * 使用 MimeMessageHelper 对MimeMail对象中添加富文本
             * multipart 是用于指定 MimeMessageHelper 是否允许使用上传技术
             * 因为附件文件需要用到上传,所以如果需要上传附件,则需要 multipart 为 true
             * 注意:MimeMessageHelper 只是用于帮助 MimeMail 方便添加富内容,非邮件对象
             * 因此,发送邮件时,应当发送的还是 MimeMessage 对象
             */
            MimeMessageHelper helper = new MimeMessageHelper(mail, true);

            helper.setFrom(from);
            helper.setTo(to);
            helper.setSubject(subTitle);
            // 此处发送正文,要求可以支持 html
            helper.setText(context, true);

            // 增加附件
            File file = new File("D:/xxx.rar");
            helper.addAttachment("附件名称", file);
            
        } catch (MessagingException e) {
            throw new RuntimeException(e);
        }

        // 发送邮件
        javaMailSender.send(mail);

    }

 

SpringBoot 整合第三方异步消息队列处理

异步消息队列,通俗的讲就是,当一个客户端向服务器请求数据的时候,服务器可以接收并马上处理这个请求,但如果发送请求的人非常多,而服务器在当前无非把所有请求全部处理,那么服务器可以把这些请求先存放到队列中,并一个个的处理完毕,Java中提出了一个异步消息队列处理的规范Jms(Java Message Service).

 

ActiveMQ

下载地址:https://activemq.apache.org/components/classic/download/

启动服务:activemq.bat

访问服务器

http://127.0.0.1:8161/

服务端口:61616,管理后台端口:8161

用户名&密码:admin

整合SpringBoot操作

1.导入ActiveMQ依赖坐标

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

 

 

2.配置文件中配置AvtiveMQ服务器地址等信息

spring:
  activemq:
    broker-url: tcp://localhost:61616
  jms:
    # 设置消息发布订阅模式,否则为 点对点 模式
    pub-sub-domain: true
    template:
      # 消息存到队列中需要有一个容器,这里定义了一个默认容器,当存放消息没有指定队列时,会默认存放默认容器中
      default-destination: unsoft

 

 

3.使用自动装配【JmsMessagingTemplate】对象,并进行操作

@Service
public class MessageServiceImpl implements MessageService {

    @Autowired
    private JmsMessagingTemplate messagingTemplate;

    @Override
    public void sendMessage(String id) {
        /**
         * 对消息数据自动转化并发送到队列中
         * 当不设定 destinationName 时,则会默认使用 默认队列
         */
        messagingTemplate.convertAndSend("unsoft.message.id", id);
    }

    @Override
    public String doMessage() {
        
        // 在队列中取得消息,并进行消息处理
        String id = messagingTemplate.receiveAndConvert("unsoft.message.id", String.class);
        return id;
    }
}

 

 

4.自动处理消息队列

我们之所以增加消息异步处理,是因为防些请求过多服务器处理不及时,而我们的服务器当接收到消息队列时,应当马上从队列中取出消息进行处理,而不是我们手动去取得消息再处理,在ActiveMQ中,可以让服务器监听队列,当队列收到消息时马上对消息进行处理。

我们创建一个用于处理队列消息的Bean类,使用【@JmsListener】

@Component
public class ActiveMQLister {

    // 定义需要监听那一个队列中的消息
    // 当这个队列收到消息后,便会马上接收并处理这个消息
    @JmsListener(destination = "unsoft.message.id")
    public void receive(String queue){
        System.out.println(queue+"消息已被处理");
    }

}

 

 

5.ActiveMQ的监听处理方而还支持消息传递,即这一个处理完成后,可以把处理的结果再加到其它队列中,由其它队列二次处理。

使用【@SendTo】注解定义,目该队列消息处理方法需要有返回值。

@Component
public class ActiveMQLister {

    // 定义需要监听那一个队列中的消息
    // 当这个队列收到消息后,便会马上接收并处理这个消息
    @JmsListener(destination = "unsoft.message.id")
    
    // 本方法处理完后,会把返回的值发送到“unsoft.order.id”中,由对应的队列处理方法接收处理
    @SendTo("unsoft.order.id")
    public String receive(String queue){
        System.out.println(queue+"消息已被处理");
        return queue;
    }
}

 

RabbitMQ

RabbitMQ 基于Erlang 语言编写,需要安装Erlang运行环境

Erlang 下载地址:https://www.erlang.org/downloads

RabbitMQ 下载地址:https://rabbitmq.com/install-windows.html

启动 RabbitMQ 后台管理系统

查看已安装的插件列表:rabbitmq-plugins.bat list

开启服务管理插件:rabbitmq-plugins.bat enable rabbitmq_management

访问服务器:http://localhost:15672

服务端口:5672,管理后台端口:15672

用户名&密码:guest

 

SpringBoot 整合 RabbitMQ

1.导入 RabbitMQ 依赖坐标

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

 

2.配置文件中对RabbitMQ进行配置

spring:
  rabbitmq:
    host: localhost
    port: 5672

 

RabbitMQ 分直连和direct模式和topic模式

direct直连交换机模式

3.定义消息队列

@Component
public class RabbitDirectConfig {

    /**
     * 定义一个队列,队列可以有多个,多个队列绑定到一个Exchange中
     * @return
     */
    
    public Queue queue() {
        // durable:是否持久化,默认false
        // exclusive:是否当前连接专用,默认false,连接关闭后队列即被删除
        // autoDelete:是否自动删除,当生产者或消费者不再使用此队列,自动删除
        return new Queue("queue1", true, true, true);
    }

    
    public Queue queue2() {
        return new Queue("queue2", true, true, true);
    }

    /**
     * 创建一个Exchange,一个Exchange可以绑定多个Queue队列
     * @return
     */
    public DirectExchange directExchange(){
        return new DirectExchange("directExchange");
    }

    /**
     * 把多个Queue绑定到Exchange中
     */
    
    @Bean
    public Binding bindingQueue1(){
        return BindingBuilder.bind(queue()).to(directExchange()).with("direct1");
    }

    @Bean
    public Binding bindingQueue2(){
        return BindingBuilder.bind(queue2()).to(directExchange()).with("direct2");
    }

}

 

4.在需要加入队列消息时,使用【AmqpTemplate】对象加入消自

@Service
public class MessageServiceRabbitMQImpl implements MessageService {

    @Autowired
    private AmqpTemplate amqpTemplate;
    
    @Override
    public void sendMessage(String id) {
        amqpTemplate.convertAndSend("directExchange", "direct1",id);
        System.out.println("消息队列已被加入");
    }

 

5.消息队列自动监听处理

@Component
public class RabbitMQListener {
    
    @RabbitListener(queues = "queue1")
    public String receive(String id){
        System.out.println("对"+ id + "进行处理");
        return id;
    }
}

注意:当有多个监听时,队列中的消自将以轮询的方式轮流负责处理,即一次给queue1,一次给queue2,一次给queue1....

 

topic主题交指机模式

topic主题交换机模式和direct直连交换机模式相似

唯一不同的是topic可以通过【routingKey】来匹配队列

@Configuration
    public class RabbitTopicConfig {
        @Bean
        public Queue topicQueue() {
            return new Queue("topic_queue");
        }

        @Bean
        public Queue topicQueue2() {
            return new Queue("topic_queue2");
        }

        @Bean
        public TopicExchange topicExchange() {
            return new TopicExchange("topicExchange");
        }

        @Bean
        public Binding bindingTopic() {
            return BindingBuilder.bind(topicQueue()).to(topicExchange()).with("topic.*.*");
        }

        @Bean
        public Binding bindingTopic2() {
            return BindingBuilder.bind(topicQueue2()).to(topicExchange()).with("topic.#");
        }
    }

其中【*】和【#】号的区别如下

*  (星号): 用来表示一个单词 ,且该单词是必须出现的

#  (井号): 用来表示任意数量

匹配键 topic.*.* topic.#
topic.order.id true true
order.topic.id false false
topic.sm.order.id false true
topic.sm.id false true
topic.id.order true true
topic.id false true
topic.order false true

 

Kafka

下载地址:https://kafka.apache.org/downloads

启动zookeeper

zookeeper-server-start.bat ..\..\config\zookeeper.properties

默认端口:2181

 

启动kafka

kafka-server-start.bat ..\..\config\server.properties

默认端口:9092

 

创建topic

kafka-topics.bat --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic unsoft

 

查看topic

kafka-topics.bat --zookeeper 127.0.0.1:2181 --list

 

删除topic

kafka-topics.bat --delete --zookeeper localhost:2181 --topic unsoft

 

生产者功能测试

kafka-console-producer.bat --broker-list localhost:9092 --topic unsoft

 

消费者功能测试

kafka-console-consumer.bat --bootstrap-server localhost:9092 --topic unsoft --from-beginning

 

SpringBoot 整合 Kafka

1.导入Kafka依赖坐标

        <dependency>
            <groupId>org.springframework.kafka</groupId>
            <artifactId>spring-kafka</artifactId>
        </dependency>

 

2.在配置文件中对kafka进行基础配置

spring:
  kafka:
    bootstrap-servers: localhost:9092
    consumer:
      group-id: unsoft # 消费者组

 

3.生产消息

@Service
public class MessageServiceKafkaImpl implements MessageService {
    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;

    @Override
    public void sendMessage(String id) {
        System.out.println("即将对消息发送到消息中间件");
        // 把 id 消息发送到 topic 为 unsoft 中
        kafkaTemplate.send("unsoft", id);
    }
}

 

4.自动接收并处理消息

@Component
public class MessageListener {

    @KafkaListener(topics = {"unsoft"})
    // Kafka 不是直接接收 id ,而是接收一个ConsumerRecord<key,value> 对象,从对象中可以取得接收的值
    public String receive(ConsumerRecord<String,String> record){
        System.out.println("接收到消息为" + record.value());
        return record.value();
    }

}

 

 

监控

SpringBoot Admin

开源社区项目,用于管理和监控SpringBoot应用程序。客户端注册到服务端后,通过HTTP请求方式,服务端定期从客户端获取对应的信息,并通过UI界面展示对应信息。

 

创建 SpringBoot Admin Server 服务器端

要监控多个应用,首先要创建一个用于收集这些应用信息的平台

参考源码:https://github.com/codecentric/spring-boot-admin

 

1.添加 SpringBoot Admin Server 服务器端依赖坐标

<properties>
        <-- 控制服务器端版本 -->
        <spring-boot-admin.version>2.7.8</spring-boot-admin.version>
    </properties>
    <dependencies>
       <-- 导入 SpringBoot Admin Server 服务器端依赖 -->
        <dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-starter-server</artifactId>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <-- 引入服务器端版本依赖信息 -->
                <groupId>de.codecentric</groupId>
                <artifactId>spring-boot-admin-dependencies</artifactId>
                <version>${spring-boot-admin.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

可以简化为,直接在依赖坐标中加入版本

 

        <dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-starter-server</artifactId>
            <version>2.7.8</version>
        </dependency>

注意:服务端版本,必须要和当前运行的SpringBoot运行版本相同。

 

 

2.配置服务端,避免与客户端应用端口产生冲突

server:
  port: 8080

 

 

3.在运行类中加入【@EnableAdminServer】注解,启用服务器端服务

@SpringBootApplication
@EnableAdminServer
public class Application { }

 

创建 SpringBoot Admin Client 客户端

1.导入 客户端 的依赖坐标

        <dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-starter-client</artifactId>
            <version>2.7.8</version>
        </dependency>

 

 

2.配置客户端

配置一:配置客户端请求监控的服务器地址

spring:
  boot:
    admin:
      client:
        url: http://localhost:8080 # 配置服务器端的地址

配置二:配置客户端允许监控的项目

 

management:
  endpoint:
    health:
      show-details: always # 显示监控信息,默认不显示
  endpoints:
    web:
      exposure:
        include: "*" # 默认开启监控的是 Health,即健康信息,“*”则表示开启所有监控允许

注意:如果不对客户端进行配置的话,那么在监控服务器中将看不到任何客户端应用的运行信息。

 

3.运行应用

应用不需要在代码中增加声明,直接运行,就可以实现监控功能。

 

Actuator 节点原理

客户端上线后会连接服务器端,服务器端收到上线请求后,会访问客户端的“/actuator”获取客户端所提供的允许监控信息,允许监控信息的节点有如下:

ID 描述 默认启用
auditevents 暴露当前应用程序的审计事件信息。
beans 显示应用程序中所有 Spring bean 的完整列表。
caches 暴露可用的缓存。
conditions 显示在配置和自动配置类上评估的条件以及它们匹配或不匹配的原因。
configprops 显示所有 @ConfigurationProperties 的校对清单。
env 暴露 Spring ConfigurableEnvironment 中的属性。
flyway 显示已应用的 Flyway 数据库迁移。
health 显示应用程序健康信息
httptrace 显示 HTTP 追踪信息(默认情况下,最后 100 个 HTTP 请求/响应交换)。
info 显示应用程序信息。
integrationgraph 显示 Spring Integration 图。

 

ID 描述 默认启用
loggers 显示和修改应用程序中日志记录器的配置。
liquibase 显示已应用的 Liquibase 数据库迁移。
metrics 显示当前应用程序的指标度量信息。
mappings 显示所有 @RequestMapping 路径的整理清单。
scheduledtasks 显示应用程序中的调度任务。
sessions 允许从 Spring Session 支持的会话存储中检索和删除用户会话。当使用 Spring Session 的响应式 Web 应用程序支持时不可用。
shutdown 正常关闭应用程序。
threaddump 执行线程 dump。

 

Web程序专用端点

ID 描述 默认启用
heapdump 返回一个 hprof 堆 dump 文件。
jolokia 通过 HTTP 暴露 JMX bean(当 Jolokia 在 classpath 上时,不适用于 WebFlux)。
logfile 返回日志文件的内容(如果已设置 logging.file 或 logging.path 属性)。支持使用 HTTP Range 头来检索部分日志文件的内容。
prometheus 以可以由 Prometheus 服务器抓取的格式暴露指标。

 

节点设置配置说明:

management:
  # 设置单个节点的监控权限
  endpoint:
    # health 节点必须监控权限必须开启,为默认开启权限,否则会报错
    health:
      enabled: true
      # 对于 health 节点而言,节点信息是否显示,默认为“never”永不开启
      show-details: always
    # beans 节点
    beans:
      # 仅对 beans 节点进行控制开关
      enabled: true
    # info 节点
    info:
      enabled: true
    # ... 可以在此单独设置需要控制的节点

  # 这里控制所有节点的整体设置
  endpoints:
    # 控制所有节点的默认开启状态,默认为true,如果为false,则不单独设置enabled时,刚默认为false关闭
    enabled-by-default: true
    # jmx 端开启监控节点
    jmx:
      exposure:
        include: "*" # jmx 默认开启所有监控节点,可以在 jconsole 中查看
    # web 端开启监控节点
    web:
      exposure:
        include: "*" # web 默认开启 health 节点,可以在 web 中查看

 

对于 jmx 和 web 的节点允许限制配置项

属性 默认
management.endpoints.jmx.exposure.exclude
management.endpoints.jmx.exposure.include *
management.endpoints.web.exposure.exclude
management.endpoints.web.exposure.include info, health

 

jmx 与 web 默认允许监控表

ID JMX Web
auditevents
beans
caches
conditions
configprops
env
flyway
health
heapdump N/A
httptrace
info

 

ID JMX Web
integrationgraph
jolokia N/A
logfile N/A
loggers
liquibase
metrics
mappings
prometheus N/A
scheduledtasks
sessions
shutdown
threaddump

 

自定义 Info 端点信息

Info 端点默认在客户端中是不提供任何数据的,因为Info端点是预留给开发者自定义数据的。

方法一:使用配置文件自定义Info

在配置文件中加入父节点为 "info" 的节点,即可自定义端点信息

info:
  AppName: "Unsoft Application"
  Version: "1.0.1"
  JavaVersion: @java.version@
  ...

 

方法二:使用配置类自定义Info

定义一个Bean类,实现接口【InfoContributor】方法

@Component
public class AppInfoContributor implements InfoContributor {
    @Override
    public void contribute(Info.Builder builder) {

        // 通过传入键和值设置 Info 端点信息
        builder.withDetail("RunTime", System.currentTimeMillis());

        // 通过传入一个Map集合设置 Info 端点信息
        Map<String, Object> info = new HashMap<>();
        info.put("Company", "Unsoft Corp");
        builder.withDetails(info);

    }
}

 

 

自定义Health端点信息

在日后的开发中,我们可能会加入各种各样的插件或第三方整合程序,我们可以把这些整合程序的状态放在Health端点中。

通过创建一个Bean类,继承【AbstractHealthIndicator】类,实现【doHealthCheck】方法创建Health端点信息

@Component
public class HealthConfig extends AbstractHealthIndicator {
    @Override
    protected void doHealthCheck(Health.Builder builder) throws Exception {
        
        if (true){
            // 自定义Health状态,可以设置 up,down,out_of_service
            builder.status(Status.UP);
            Map<String,Object> status = new HashMap<>();
            status.put("xxx插件","已启动成功");
            status.put("xxx版本","1.0.0");
            builder.withDetails(status);
            
        }else {
            // 服务启动失败时
            builder.status(Status.DOWN);
            // 也可以设置 out_of_service(超出服务) 状态
            builder.withDetail("xxx插件","已启动失败");
        }
        
    }
}

注意:如果不设置status时,其状态为 UNKNOWN ,不计入状态情况

 

 

自定义 Metrics 端点信息

Metrics 端点是SpringBoot Admin 中的性能计算统计端点,如下

我们也可以自定义它的计算统计

 

具体操作:

1.在需要统计调用数的类中定义一个有参构造,参数为【MeterRegistry】

@Service
public class MessageServiceKafkaImpl implements MessageService {

    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;

    private Counter counter;
    // 定义一个有参构造函数,接收一个MeterRegistry对象
    public MessageServiceKafkaImpl(MeterRegistry meterRegistry){
        counter = meterRegistry.counter("sendMessage.Count");
    }

    @Override
    public void sendMessage(String id) {
        System.out.println("即将对消息发送到消息中间件");
        // 把 id 消息发送到 topic 为 unsoft 中
        kafkaTemplate.send("unsoft", id);

        // 在需要统计的方法中增加该统计数量
        counter.increment();
    }
}

 

 

自定义端点

与Info类似的端点,我们也可以自定义

创建一个用于定义自定义端点的Bean类

@Component
// 定义自定义端点,并设置默认开启端点监控权限
@Endpoint(id = "自定义端点名称", enableByDefault = true)

public class OtherEndPoint {

    /**
     * 创建一个用于返回自定义端点的方法
     * 添加注解声明,ReadOperation 是用于读取端点信息的注解,当请求时会执行带有ReadOperation注解的方法
     *
     * @return
     */
    @ReadOperation
    public Object getPointInfo() {
        Map<String, Object> point = new HashMap<>();
        point.put("pointName1", 1);
        point.put("pointName2", 2);
        point.put("pointName3", 3);
        return point;
    }

}

 

 

如果您喜欢本站,点击这儿不花一分钱捐赠本站

这些信息可能会帮助到你: 下载帮助 | 报毒说明 | 进站必看

修改版本安卓软件,加群提示为修改者自留,非本站信息,注意鉴别

THE END
分享
二维码
打赏
海报
Java – SpringBoot – 进阶整合实例
简介 本文主要讲解SpringBoot项目的系统部署方面的进阶教程,基础整合教程可阅读下面文章 Java – SpringBoot – 基础整合实……
<<上一篇
下一篇>>