学习SpringBoot

整合第三方技术

缓存

SpringBoot内置缓存解决方案

spring Boot 提供了多种内置缓存解决方案,通过简单的配置即可快速为应用程序添加缓存功能。

1. Spring Cache 抽象层

Spring Boot 提供了一个统一的缓存抽象层 spring-context 中的 CacheCacheManager,它不提供具体的缓存实现,而是定义了一套缓存操作的规范。

主要特性

  • 声明式缓存:通过注解方式使用缓存
  • 支持多种缓存提供商
  • 提供缓存操作的统一 API

步骤①:添加必要的依赖

复制代码
<!-- pom.xml -->
<dependencies>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- Spring Boot Starter Cache -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>
    
    <!-- 使用 Caffeine 作为缓存实现 -->
    <dependency>
        <groupId>com.github.ben-manes.caffeine</groupId>
        <artifactId>caffeine</artifactId>
        <version>3.1.8</version>
    </dependency>
    
    <!-- Lombok 简化代码 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

or

步骤②:启用缓存,在应用程序的主类上添加 @EnableCaching 注解来启用缓存功能

复制代码
// Application.java
@SpringBootApplication
@EnableCaching
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
    
  }

步骤③:设置操作的数据是否使用缓存

复制代码
@Service
public class BookServiceImpl implements BookService {
    @Autowired
    private BookDao bookDao;

    @Cacheable(value="cacheSpace",key="#id")
    public Book getById(Integer id) {
        return bookDao.selectById(id);
    }
}
2. 内置缓存实现

Spring Boot 自动配置了多种缓存实现,只需添加相应的依赖即可启用:

2.1 Simple Cache

Spring Boot 默认提供的简单内存缓存实现,基于 ConcurrentHashMap

特点

  • 轻量级,无需额外依赖
  • 适合开发和测试环境
  • 不适合生产环境,因为重启应用会丢失所有缓存数据
2.2 EhCache

EhCache 是一个功能强大、高性能的 Java 缓存框架。

特点

  • 支持内存和磁盘存储
  • 支持缓存数据的持久化
  • 适合生产环境使用

步骤①:添加必要的依赖

复制代码
<!-- pom.xml -->
<dependencies>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- Spring Boot Starter Cache -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>
    
    <!-- Ehcache 依赖 -->
    <dependency>
        <groupId>org.ehcache</groupId>
        <artifactId>ehcache</artifactId>
        <version>3.10.8</version>
    </dependency>
    
    <!-- Lombok 简化代码 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

步骤②:配置缓存技术实现使用Ehcache

复制代码
# src/main/resources/application.yml
server:
  port: 8080

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

步骤③:指定ehcache的配置文件

src/main/resources 目录下创建 ehcache.xml 文件:

复制代码
<!-- src/main/resources/ehcache.xml -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://www.ehcache.org/v3"
        xsi:schemaLocation="http://www.ehcache.org/v3 http://www.ehcache.org/v3/ehcache.xsd">

    <!-- 默认缓存配置 -->
    <cache alias="default">
        <expiry>
            <ttl unit="minutes">30</ttl>
        </expiry>
        <heap unit="entries">2000</heap>
    </cache>

    <!-- 用户缓存配置 -->
    <cache alias="users">
        <expiry>
            <ttl unit="minutes">10</ttl>
        </expiry>
        <heap unit="entries">500</heap>
        <disk unit="gb">1</disk>
        <offheap unit="mb">100</offheap>
    </cache>

    <!-- 产品缓存配置 -->
    <cache alias="products">
        <expiry>
            <ttl unit="minutes">20</ttl>
        </expiry>
        <heap unit="entries">1000</heap>
        <disk unit="gb">5</disk>
        <offheap unit="mb">200</offheap>
    </cache>

    <!-- 短期缓存配置 -->
    <cache alias="shortTerm">
        <expiry>
            <ttl unit="seconds">60</ttl>
        </expiry>
        <heap unit="entries">100</heap>
    </cache>
</config>
2.3 Caffeine

Caffeine 是一个高性能的 Java 缓存库,被认为是 Guava Cache 的改进版本。

特点

  • 高性能
  • 自动加载
  • 基于时间的失效策略
  • 支持异步刷新
2.4 Redis Cache

Redis 是一个开源的内存数据结构存储系统,常用作数据库、缓存和消息代理。

特点

  • 高性能
  • 支持多种数据结构
  • 持久化支持
  • 分布式缓存
2.5 Hazelcast

Hazelcast 是一个开源的内存数据网格,提供分布式数据结构和计算功能。

特点

  • 分布式缓存
  • 高可用性
  • 自动集群发现
  • 支持数据分区和复制
3.Spring Boot 整合 Memcached 缓存

步骤①:添加必要的依赖

复制代码
<!-- pom.xml -->
<dependencies>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- Spring Boot Starter Cache -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>
    
    <!-- Spring Boot Data Redis (用于连接 Memcached) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    
    <!-- Xmemcached 客户端 -->
    <dependency>
        <groupId>com.googlecode.xmemcached</groupId>
        <artifactId>xmemcached</artifactId>
        <version>2.4.7</version>
    </dependency>
    
    <!-- Lombok 简化代码 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

步骤②:安装和启动 Memcached 服务器

安装 Memcached

Windows 系统:

  1. 下载 Memcached for Windows: https://memcached.org/downloads

  2. 解压下载的文件

  3. 打开命令提示符,导航到解压目录

  4. 运行 memcached.exe -d install 安装为服务

  5. 运行 memcached.exe -d start 启动服务

配置 Memcached

Windows: 编辑 memcached.ini 文件

例如,修改监听地址和端口:

复制代码
-l 127.0.0.1
-p 11211

步骤③:配置 Spring Boot 连接 Memcached

复制代码
# src/main/resources/application.yml
server:
  port: 8080

spring:
  cache:
    type: simple
    cache-names: users,products,shortTerm

# Memcached 配置
memcached:
  # Memcached 服务器地址
  servers: 127.0.0.1:11211
  # 连接池大小
  connectionPoolSize: 10
  # 操作超时时间(毫秒)
  opTimeout: 3000
  # 连接超时时间(毫秒)
  connectTimeout: 3000
  # 获取连接超时时间(毫秒)
  timeoutExceptionThreshold: 2000
  # 是否使用二进制协议
  binaryProtocol: true
  # 哈希算法
  hashAlgorithm: KETAMA_HASH
  # 默认过期时间(秒)
  defaultExpiration: 3600
  # 是否使用朴素编码器
  primitiveAsString: false

步骤④:创建自定义的 Memcached 缓存管理器

步骤⑤:启用缓存功能 @EnableCaching

步骤⑥:使用 @Cacheable@CachePut@CacheEvict 等注解进行缓存操作

步骤⑦:实现缓存监控以了解缓存性能

4.SpringBoot整合jetcache缓存

JetCache 是一个优秀的开源缓存框架,由阿里巴巴开发并开源,它提供了统一的缓存 API,支持本地缓存和分布式缓存,并且具有丰富的功能如二级缓存、缓存自动刷新等。

目前jetcache支持的缓存方案本地缓存支持两种,远程缓存支持两种,分别如下:

  • 本地缓存(Local)
    • LinkedHashMap
    • Caffeine
  • 远程缓存(Remote)
    • Redis
    • Tair

LinkedHashMap+Redis的方案:

步骤①:添加必要的依赖

复制代码
<!-- pom.xml -->
<dependencies>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- Spring Boot Starter Cache -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>
    
    <!-- JetCache 依赖 -->
    <dependency>
        <groupId>com.alicp.jetcache</groupId>
        <artifactId>jetcache-starter-redis</artifactId>
        <version>2.6.2</version>
    </dependency>
    
    <!-- Redis 依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    
    <!-- Lombok 简化代码 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

步骤②:配置 Redis 和 JetCache

复制代码
# src/main/resources/application.yml
server:
  port: 8080

spring:
  cache:
    type: none  # 禁用 Spring 原生的缓存,使用 JetCache

  # Redis 配置
  redis:
    host: 127.0.0.1
    port: 6379
    password: 
    database: 0
    lettuce:
      pool:
        max-active: 100
        max-idle: 10
        min-idle: 2
        max-wait: 1000

# JetCache 配置
jetcache:
  # 默认配置
  default:
    # 缓存类型:Redis
    type: redis
    # Redis 配置
    host: 127.0.0.1
    port: 6379
    password: 
    database: 0
    # 本地缓存配置
    local:
      # 本地缓存类型:LinkedHashMap
      type: linkedhashmap
      # 本地缓存过期时间,单位秒
      expireAfterWrite: 300
      # 本地缓存最大条目数
      limit: 100
    # 远程缓存过期时间,单位秒
    expireAfterWrite: 3600
    # 远程缓存最大条目数
    limit: 1000
    # 缓存 key 的前缀
    keyPrefix: jetcache_
    # 是否缓存 null 值
    cacheNullValue: true
    # 是否启用统计
    areaInCacheName: false
  # 自定义缓存区域
  areas:
    # 用户缓存区域
    userCache:
      type: redis
      local:
        type: linkedhashmap
        expireAfterWrite: 300
        limit: 100
      expireAfterWrite: 1800
      limit: 500
      keyPrefix: user_cache_
    # 产品缓存区域
    productCache:
      type: redis
      local:
        type: linkedhashmap
        expireAfterWrite: 600
        limit: 200
      expireAfterWrite: 3600
      limit: 1000
      keyPrefix: product_cache_

步骤③: 创建 JetCache 配置类

启用缓存,在引导类上方标注注解@EnableCreateCacheAnnotation配置springboot程序中可以使用注解的形式创建缓存

步骤④:创建服务类

创建缓存对象Cache,并使用注解@CreateCache标记当前缓存的信息,然后使用Cache对象的API操作缓存,put写缓存,get读缓存。

​ jetcache提供了方法缓存方案,只不过名称变更了而已。在对应的操作接口上方使用注解@Cached即可

复制代码
 /**
     * 使用 @Cached 注解缓存方法结果
     * 首次调用时执行方法体并将结果缓存,后续调用直接从缓存获取
     */
    @Cached(expire = 3600, cacheType = CacheType.BOTH)
    public User getUserById(Long id) {
        System.out.println("从数据库获取用户: " + id);
        // 模拟数据库查询延迟
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return userDatabase.get(id);
    }
    
    /**
     * 使用 @Cached 注解,并指定缓存区域
     */
    @Cached(expire = 1800, cacheType = CacheType.BOTH, area = "userCache")
    public User getUserByIdWithArea(Long id) {
        System.out.println("从数据库获取用户(指定缓存区域): " + id);
        return userDatabase.get(id);
    }

  /**
     * 使用 @CacheUpdate 注解更新缓存
     * 每次调用都会执行方法体并更新缓存
     */
    @CacheUpdate(expire = 3600, cacheType = CacheType.BOTH)
    public User updateUser(User user) {
        System.out.println("更新用户: " + user.getId());
        userDatabase.put(user.getId(), user);
        return user;
    }
    
    /**
     * 使用 @CacheUpdate 注解,并指定缓存区域
     */
    @CacheUpdate(expire = 1800, cacheType = CacheType.BOTH, area = "userCache")
    public User updateUserWithArea(User user) {
        System.out.println("更新用户(指定缓存区域): " + user.getId());
        userDatabase.put(user.getId(), user);
        return user;
    }
    
    /**
     * 使用 @CacheInvalidate 注解移除缓存
     * 方法执行后移除指定缓存
     */
    @CacheInvalidate
    public void deleteUser(Long id) {
        System.out.println("删除用户: " + id);
        userDatabase.remove(id);
    }
    
    /**
     * 使用 @CacheInvalidate 注解,并指定缓存区域
     */
    @CacheInvalidate(area = "userCache")
    public void deleteUserWithArea(Long id) {
        System.out.println("删除用户(指定缓存区域): " + id);
        userDatabase.remove(id);
    }
    
    /**
     * 使用 @CacheInvalidate 注解移除所有缓存
     * keyPattern 使用通配符匹配多个 key
     */
    @CacheInvalidate(keyPattern = "user_*")
    public void clearAllUsers() {
        System.out.println("清除所有用户缓存");
    }
    
    /**
     * 使用 @CachePenetrate 注解处理缓存穿透
     * 当缓存和数据库都不存在值时,返回默认值而不是 null
     */
    @CachePenetrate(expire = 3600, cacheType = CacheType.BOTH, defaultValue = "default_user")
    public User getUserWithPenetration(Long id) {
        System.out.println("从数据库获取用户(处理穿透): " + id);
        return userDatabase.get(id);
    }

步骤⑤:

5.SpringBoot整合j2cache缓存

步骤①:添加必要的依赖

复制代码
<!-- pom.xml -->
<dependencies>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- Spring Boot Starter Cache -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>
    
    <!-- J2Cache 依赖 -->
    <dependency>
        <groupId>net.oschina.j2cache</groupId>
        <artifactId>j2cache-core</artifactId>
        <version>2.8.4-release</version>
    </dependency>
    
    <!-- Ehcache 依赖 -->
    <dependency>
        <groupId>org.ehcache</groupId>
        <artifactId>ehcache</artifactId>
        <version>3.9.7</version>
    </dependency>
    
    <!-- Redis 依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    
    <!-- Lombok 简化代码 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

步骤②: 配置 Redis 和 J2Cache

创建 J2Cache 配置文件

配置一级与二级缓存,并配置一二级缓存间数据传递方式,在 src/main/resources 目录下创建 j2cache.properties 文件:

复制代码
# src/main/resources/j2cache.properties
# 缓存提供者配置 1: ehcache, 2: redis, 3: caffeine, 4: none
j2cache.L1_provider_class=net.oschina.j2cache.cache.support.ehcache.EhcacheCacheProvider
j2cache.L2_provider_class=net.oschina.j2cache.cache.support.redis.RedisCacheProvider
j2cache.default_cache_region=__default_region__
j2cache.default_serialization=fastjson
j2cache.autoRefreshCache=false
j2cache.openCacheStatistics=true
j2cache.timeToLiveSeconds=3600
j2cache.timeToIdleSeconds=600
j2cache.cache_event_listener_channel=j2cache_chan

# Redis 配置
redis.host=127.0.0.1
redis.port=6379
redis.password=
redis.database=0
redis.timeout=3000
redis.maxActive=1000
redis.maxIdle=100
redis.minIdle=10
redis.testOnBorrow=true

# Ehcache 配置
ehcache.configXml=ehcache.xml

步骤③:

任务

Quartz

Quartz 任务调度框架介绍

Quartz 是一个功能强大、开源的作业调度库,可以与几乎任何 Java 应用程序集成,从最小的独立应用到最大的电子商务系统。Quartz 完全由 Java 编写,具有丰富的功能集,支持集群和事务。

Spring Boot 整合 Quartz

步骤①:添加必要的依赖

复制代码
<!-- pom.xml -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

步骤②:创建任务类并继承QuartzJobBean

复制代码
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Component;

@Component
public class MyQuartzJob extends QuartzJobBean {
    
    // 可以注入其他 Spring Bean
    @Autowired
    private SomeService someService;
    
    // 可选:初始化方法
    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        // 获取 JobDataMap 中的数据
        JobDataMap jobDataMap = context.getMergedJobDataMap();
        String message = jobDataMap.getString("message");
        
        // 执行任务逻辑
        System.out.println("执行任务: " + new Date());
        System.out.println("消息: " + message);
        
        // 调用注入的 Service
        someService.doSomething();
    }
}

步骤③:在 Spring 配置中定义 JobDetail 和 Trigger

复制代码
import org.quartz.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.scheduling.quartz.SimpleTriggerFactoryBean;

@Configuration
public class QuartzConfig {
    
    /**
     * 配置 JobDetail
     */
    @Bean
    public JobDetailFactoryBean myJobDetail() {
        JobDetailFactoryBean jobDetailFactoryBean = new JobDetailFactoryBean();
        jobDetailFactoryBean.setJobClass(MyQuartzJob.class);
        jobDetailFactoryBean.setJobDataMap(new JobDataMap());
        
        // 设置任务数据
        jobDetailFactoryBean.getJobDataMap().put("message", "Hello Quartz!");
        
        // 设置任务名称和组
        jobDetailFactoryBean.setName("myJob");
        jobDetailFactoryBean.setGroup("myGroup");
        
        // 设置持久化
        jobDetailFactoryBean.setDurability(true);
        
        return jobDetailFactoryBean;
    }
    
    /**
     * 配置 SimpleTrigger
     */
    @Bean
    public SimpleTriggerFactoryBean myJobTrigger() {
        SimpleTriggerFactoryBean triggerFactoryBean = new SimpleTriggerFactoryBean();
        triggerFactoryBean.setJobDetail(myJobDetail().getObject());
        triggerFactoryBean.setStartTime(new Date());
        triggerFactoryBean.setRepeatInterval(3000); // 3秒执行一次
        triggerFactoryBean.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY);
        triggerFactoryBean.setName("myJobTrigger");
        return triggerFactoryBean;
    }
    
    /**
     * 配置 CronTrigger
     */
    @Bean
    public CronTriggerFactoryBean myCronJobTrigger() {
        CronTriggerFactoryBean triggerFactoryBean = new CronTriggerFactoryBean();
        triggerFactoryBean.setJobDetail(myJobDetail().getObject());
        triggerFactoryBean.setCronExpression("0/5 * * * * ?"); // 每5秒执行一次
        triggerFactoryBean.setName("myCronJobTrigger");
        return triggerFactoryBean;
    }
    
    /**
     * 配置调度器
     */
    @Bean
    public SchedulerFactoryBean schedulerFactoryBean() {
        SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
        schedulerFactoryBean.setJobDetails(myJobDetail().getObject());
        
        // 可以配置多个触发器
        schedulerFactoryBean.setTriggers(
            myJobTrigger().getObject(),
            myCronJobTrigger().getObject()
        );
        
        // 自动启动调度器
        schedulerFactoryBean.setAutoStartup(true);
        
        return schedulerFactoryBean;
    }
}

Task

Spring Task 介绍

Spring Task 是 Spring 框架提供的一个轻量级任务调度工具,它允许你在 Spring 应用中执行定时任务。Spring Task 基于 Java 的 java.util.concurrent.ScheduledExecutorService 实现,提供了简单易用的注解和配置方式来创建和管理定时任务。

Spring Task 是 Spring 框架的一部分,无需额外依赖即可使用。它提供了以下功能:

  • 基于注解的定时任务定义

  • 基于配置文件的定时任务定义

  • 支持多种任务调度方式(固定延迟、固定频率、Cron 表达式)

  • 支持异步任务执行

  • 支持任务参数传递

  • 支持任务执行状态监控

基本使用

步骤①:需要在 Spring Boot 应用中启用任务调度功能

复制代码
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.stereotype.Component;

@SpringBootApplication
@EnableScheduling  // 启用任务调度
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

步骤②:创建定时任务

使用 @Scheduled 注解创建定时任务:

复制代码
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.Date;

@Component
public class ScheduledTasks {
    
    /**
     * 固定延迟执行:每次任务执行完成后,等待固定时间再执行下一次
     * 单位:毫秒
     */
    @Scheduled(fixedDelay = 5000)
    public void taskWithFixedDelay() {
        System.out.println("固定延迟任务执行时间: " + new Date());
    }
    
    /**
     * 固定频率执行:每隔固定时间执行一次,不管任务执行耗时多久
     * 单位:毫秒
     */
    @Scheduled(fixedRate = 3000)
    public void taskWithFixedRate() {
        System.out.println("固定频率任务执行时间: " + new Date());
        // 模拟任务耗时
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 初始延迟执行:应用启动后等待一段时间再开始执行
     */
    @Scheduled(initialDelay = 2000, fixedRate = 5000)
    public void taskWithInitialDelay() {
        System.out.println("初始延迟任务执行时间: " + new Date());
    }
    
    /**
     * Cron 表达式执行:使用 Cron 表达式定义复杂的执行时间
     */
    @Scheduled(cron = "0/10 * * * * ?")
    public void taskWithCron() {
        System.out.println("Cron 任务执行时间: " + new Date());
    }
}

步骤③:配置选项

复制代码
spring:
  task:
    scheduling:
      enabled: true
      pool:
        size: 10
      thread-name-prefix: scheduling-
      shutdown:
        await-termination: true
        await-termination-period: 60

邮件

springboot整合javamail

JavaMail 是 Java 平台用于处理电子邮件的标准 API,而 Spring Boot 通过 spring-boot-starter-mail 提供了对 JavaMail 的简化支持。

步骤①: 添加依赖

复制代码
<!-- pom.xml -->
<dependencies>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- Spring Boot Starter Mail -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-mail</artifactId>
    </dependency>
    
    <!-- Lombok 简化代码 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

步骤②:配置邮件服务器

复制代码
spring:
  mail:
    host: smtp.example.com
    port: 587
    username: your-email@example.com
    password: your-password
    protocol: smtp
    properties:
      mail:
        smtp:
          auth: true
          starttls:
            enable: true
        connectiontimeout: 5000
        timeout: 5000
        writetimeout: 5000
    default-from: your-email@example.com

步骤③:使用JavaMailSender接口发送邮件

复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Service;

@Service
public class SendMailServiceImpl implements SendMailService {
    
    @Autowired
    private JavaMailSender javaMailSender;
    
    // 发送人
    private String from = "test@qq.com";
    // 接收人
    private String to = "test@126.com";
    // 标题
    private String subject = "测试邮件";
    // 正文
    private String context = "测试邮件正文内容";
    
    @Override
    public void sendMail() {
        // 创建邮件消息对象
        SimpleMailMessage message = new SimpleMailMessage();
        
        // 设置发件人,可以添加发件人名称
        message.setFrom(from + "(小甜甜)");
        
        // 设置收件人
        message.setTo(to);
        
        // 设置邮件标题
        message.setSubject(subject);
        
        // 设置邮件内容
        message.setText(context);
        
        // 发送邮件
        javaMailSender.send(message);
    }
}

消息

Java提供了多种处理消息的标准规范和API,涵盖了不同类型的消息处理需求,如消息队列、企业消息传递、即时消息等。以下是Java中处理消息的主要标准规范:

  • JMS

  • AMQP

  • MQTT

JMS

1.1 概述

Java Message Service (JMS) 是 Java 平台上用于企业级消息传递的 API 规范,它提供了一种标准的、与厂商无关的方式来创建、发送、接收和读取消息。JMS 是 Java EE 规范的一部分,但也可以在 Java SE 环境中使用。

1.2 核心概念
  • 消息生产者 (Message Producer):创建并发送消息的客户端

  • 消息消费者 (Message Consumer):接收并处理消息的客户端

  • 消息 (Message):在系统间传递的数据对象

  • 目的地 (Destination):消息被发送或接收的地方,可以是队列或主题

  • 连接工厂 (Connection Factory):用于创建连接的工厂

  • 连接 (Connection):客户端与消息提供者的通信链路

  • 会话 (Session):单线程上下文,用于生产者和消费者创建消息和接收消息

1.3 消息模式

JMS 支持两种消息模式:

  1. 点对点模式 (P2P)

    • 每个消息只有一个消费者

    • 消费者确认消息后,消息从队列中删除

    • 常用队列 (Queue) 作为目的地

  2. 发布/订阅模式 (Pub/Sub)

    • 每个消息可以有多个消费者

    • 消息不会被删除,除非设置了过期时间

    • 常用主题 (Topic) 作为目的地

1.4 消息类型

JMS 支持多种类型的消息:

  1. TextMessage:文本消息

  2. MapMessage:键值对集合

  3. ObjectMessage:序列化对象

  4. BytesMessage:二进制数据

  5. StreamMessage:原始值流

AMQP

2.1 概述

AMQP (Advanced Message Queuing Protocol) 是一个开放标准的、面向消息的中间件协议,它提供了一种可靠、高效的消息传递机制。AMQP 协议设计之初就考虑了企业级应用的需求,具有消息路由、队列、可靠性、安全性和事务支持等特性。

2.2 核心概念
  • 消息 (Message):应用程序间传递的数据单元

  • 生产者 (Producer):创建并发送消息的客户端

  • 消费者 (Consumer):接收并处理消息的客户端

  • 代理 (Broker):消息中间件服务器,负责消息的路由和存储

  • 队列 (Queue):消息的容器,消息被发送到队列并被消费者消费

  • 交换器 (Exchange):接收生产者的消息并将其路由到一个或多个队列

  • 绑定 (Binding):交换器和队列之间的规则,定义消息如何路由

  • 路由键 (Routing Key):用于决定消息如何路由的键值

2.3 AMQP 模型

AMQP 主要包含以下组件:

  1. 连接 (Connection):客户端与代理之间的 TCP 连接

  2. 通道 (Channel):连接内的虚拟通道,允许多个线程共享一个连接

  3. 交换器 (Exchange):消息的接收者,根据绑定规则将消息路由到队列

  4. 队列 (Queue):消息的存储容器

  5. 消费者 (Consumer):从队列中获取消息的客户端

2.4 交换器类型

AMQP 支持多种交换器类型:

  1. 直接交换器 (Direct Exchange):根据路由键精确匹配

  2. 主题交换器 (Topic Exchange):根据通配符匹配路由键

  3. 扇出交换器 (Fanout Exchange):广播消息到所有绑定的队列

  4. 头交换器 (Headers Exchange):根据消息头属性匹配

MQTT

3.1 概述

MQTT (Message Queuing Telemetry Transport) 是一种轻量级的发布/订阅消息传输协议,专为低带宽、高延迟或不稳定的网络环境设计。MQTT 协议最初由 IBM 开发,现在由 OASIS 标准组织维护,是物联网 (IoT) 领域最常用的消息协议之一。

3.2 核心概念
  • 发布者 (Publisher):发布消息的客户端
  • 订阅者 (Subscriber):接收消息的客户端
  • 代理 (Broker):消息服务器,负责接收和转发消息
  • 主题 (Topic):消息的分类标识,用于路由消息
  • 服务质量 (QoS):消息传递的可靠性级别
  • 客户端 ID (Client ID):唯一标识客户端的字符串
3.3 MQTT 模型

MQTT 采用发布/订阅模型,主要组件包括:

  1. 发布者 (Publisher):将消息发布到特定主题
  2. 订阅者 (Subscriber):订阅特定主题并接收消息
  3. 代理 (Broker):接收发布者的消息并将其转发给订阅者
  4. 主题 (Topic):层次化的主题名称,用于消息路由
3.4 服务质量 (QoS) 级别

MQTT 定义了三个服务质量级别:

  1. QoS 0:最多一次 (At most once) - 消息最多传递一次,可能丢失
  2. QoS 1:至少一次 (At least once) - 消息至少传递一次,可能重复
  3. QoS 2:恰好一次 (Exactly once) - 消息恰好传递一次,不丢失不重复

SpringBoot整合ActiveMQ

ActiveMQ 是 Apache 软件基金会开发的开源消息中间件,实现了 JMS (Java Message Service) 规范。Spring Boot 提供了便捷的方式来整合 ActiveMQ,使得在 Spring Boot 应用中发送和接收消息变得非常简单。

步骤①:添加依赖

复制代码
<dependencies>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- Spring Boot Starter ActiveMQ -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-activemq</artifactId>
    </dependency>
    
    <!-- Lombok 简化代码 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

步骤②:配置 ActiveMQ

复制代码
spring:
  activemq:
    broker-url: tcp://localhost:61616
    user: admin
    password: admin
    packages:
      trust-all: true
    pool:
      enabled: true
      max-connections: 10
  jms:
    pub-sub-domain: queue

步骤③:使用JmsMessagingTemplate操作ActiveMQ

复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Service;

import javax.jms.Queue;
import javax.jms.Topic;

@Service
public class MessageSender {

    @Autowired
    private JmsTemplate jmsTemplate;

    @Autowired
    private Queue queue;

    @Autowired
    private Topic topic;

    /**
     * 发送队列消息
     */
    public void sendQueueMessage(String message) {
        jmsTemplate.convertAndSend(queue, message);
        System.out.println("发送队列消息: " + message);
    }

    /**
     * 发送主题消息
     */
    public void sendTopicMessage(String message) {
        jmsTemplate.convertAndSend(topic, message);
        System.out.println("发送主题消息: " + message);
    }

    /**
     * 延迟发送队列消息
     */
    public void sendDelayQueueMessage(String message, long delay) {
        jmsTemplate.convertAndSend(queue, message, msg -> {
            msg.setJMSType(String.class.getName());
            msg.setLongProperty("JMSTimestamp", System.currentTimeMillis() + delay);
            return msg;
        });
        System.out.println("发送延迟队列消息: " + message + ", 延迟时间: " + delay + "毫秒");
    }
}

步骤④:创建消息接收服务

创建一个服务类用于接收消息:

复制代码
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Service;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;

@Service
public class MessageReceiver {

    /**
     * 接收队列消息
     */
    @JmsListener(destination = "springboot.queue", containerFactory = "queueListenerFactory")
    public void receiveQueueMessage(String message) {
        System.out.println("接收到队列消息: " + message);
    }

    /**
     * 接收主题消息
     */
    @JmsListener(destination = "springboot.topic", containerFactory = "topicListenerFactory")
    public void receiveTopicMessage(String message) {
        System.out.println("接收到主题消息: " + message);
    }

    /**
     * 接收消息并处理消息头
     */
    @JmsListener(destination = "springboot.queue", containerFactory = "queueListenerFactory")
    public void receiveMessageWithHeader(Message message) throws JMSException {
        TextMessage textMessage = (TextMessage) message;
        String messageContent = textMessage.getText();
        long timestamp = message.getLongProperty("JMSTimestamp");
        
        System.out.println("接收到消息: " + messageContent);
        System.out.println("消息时间戳: " + timestamp);
    }
}

SpringBoot整合RabbitMQ

步骤①:添加依赖

复制代码
<dependencies>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- Spring Boot Starter AMQP -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-amqp</artifactId>
    </dependency>
    
    <!-- Lombok 简化代码 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

步骤②:配置 RabbitMQ

复制代码
spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
    virtual-host: /
    connection-timeout: 15000
    publisher-confirms: true
    publisher-returns: true
    template:
      mandatory: true
      receive-timeout: 5000
    listener:
      simple:
        acknowledge-mode: manual
        concurrency: 3
        max-concurrency: 10
        prefetch: 5

步骤③:配置 RabbitMQ 连接工厂和模板

创建一个配置类来设置 RabbitMQ 相关的 Bean:

复制代码
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RabbitMQConfig {

    /**
     * 定义队列
     */
    @Bean
    public Queue springBootQueue() {
        return new Queue("springboot.queue", true);
    }

    /**
     * 定义主题交换器
     */
    @Bean
    public TopicExchange springBootExchange() {
        return new TopicExchange("springboot.exchange");
    }

    /**
     * 定义绑定关系
     */
    @Bean
    public Binding binding() {
        return BindingBuilder.bind(springBootQueue())
                .to(springBootExchange())
                .with("springboot.routing.key");
    }

    /**
     * 配置 RabbitTemplate
     */
    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        
        // 消息转换器
        rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
        
        // 消息发送确认
        rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
            if (ack) {
                System.out.println("消息发送成功: " + correlationData.getId());
            } else {
                System.out.println("消息发送失败: " + correlationData.getId() + ", 原因: " + cause);
            }
        });
        
        // 消息退回回调
        rabbitTemplate.setReturnsCallback(returned -> {
            System.out.println("消息被退回: " + returned.getMessage());
            System.out.println("应答码: " + returned.getReplyCode());
            System.out.println("应答文本: " + returned.getReplyText());
            System.out.println("交换器: " + returned.getExchange());
            System.out.println("路由键: " + returned.getRoutingKey());
        });
        
        return rabbitTemplate;
    }
}

步骤④:创建消息发送服务

创建一个服务类用于发送消息:

复制代码
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.Map;

@Service
public class RabbitMQSender {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 发送简单消息
     */
    public void send(String message) {
        System.out.println("发送时间: " + new Date() + ",消息内容: " + message);
        rabbitTemplate.convertAndSend("springboot.queue", message);
    }

    /**
     * 发送带过期时间的消息
     */
    public void sendWithTTL(String message, long ttl) {
        rabbitTemplate.convertAndSend("springboot.queue", message, new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message, MessageProperties messageProperties) throws AmqpException {
                messageProperties.setExpiration(String.valueOf(ttl));
                return message;
            }
        });
    }

    /**
     * 发送延迟消息(通过死信队列实现)
     */
    public void sendDelayMessage(String message, long delay) {
        rabbitTemplate.convertAndSend("springboot.delay.exchange", "springboot.delay.routing.key", 
            msg -> {
                msg.getMessageProperties().setHeader("x-delay", delay);
                return msg;
            });
        System.out.println("发送延迟消息: " + message + ", 延迟时间: " + delay + "毫秒");
    }

    /**
     * 发送对象消息
     */
    public void sendObject(User user) {
        rabbitTemplate.convertAndSend("springboot.queue", user);
        System.out.println("发送对象消息: " + user);
    }

    /**
     * 发送带header的消息
     */
    public void sendWithHeader(String message, Map<String, Object> headers) {
        MessageProperties messageProperties = new MessageProperties();
        headers.forEach(messageProperties::setHeader);
        Message msg = new Message(message.getBytes(), messageProperties);
        rabbitTemplate.send("springboot.queue", msg);
    }
}

步骤⑤:创建消息接收服务

创建一个服务类用于接收消息:

复制代码
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.Date;
import java.util.Map;

@Service
public class RabbitMQReceiver {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 接收简单消息
     */
    @RabbitListener(queues = "springboot.queue")
    public void receive(String message) {
        System.out.println("接收时间: " + new Date() + ",消息内容: " + message);
    }

    /**
     * 接收消息并手动确认
     */
    @RabbitListener(queues = "springboot.queue")
    public void receiveWithAck(Message message) {
        try {
            String msg = new String(message.getBody());
            System.out.println("接收时间: " + new Date() + ",消息内容: " + msg);
            
            // 手动确认消息
            rabbitTemplate.execute(channel -> {
                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
                return null;
            });
        } catch (Exception e) {
            e.printStackTrace();
            // 拒绝消息,不重新入队
            rabbitTemplate.execute(channel -> {
                try {
                    channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
                return null;
            });
        }
    }

    /**
     * 接收对象消息
     */
    @RabbitListener(queues = "springboot.queue")
    public void receiveObject(User user) {
        System.out.println("接收对象消息: " + user);
    }

    /**
     * 接带header的消息
     */
    @RabbitListener(queues = "springboot.queue")
    public void receiveWithHeader(Message message) {
        try {
            String msg = new String(message.getBody());
            Map<String, Object> headers = message.getMessageProperties().getHeaders();
            
            System.out.println("接收时间: " + new Date() + ",消息内容: " + msg);
            System.out.println("消息头: " + headers);
            
            // 手动确认消息
            rabbitTemplate.execute(channel -> {
                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
                return null;
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

SpringBoot整合RocketMQ

步骤①:添加依赖

复制代码
<dependencies>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- Spring Boot Starter RocketMQ -->
    <dependency>
        <groupId>org.apache.rocketmq</groupId>
        <artifactId>rocketmq-spring-boot-starter</artifactId>
        <version>2.2.3</version>
    </dependency>
    
    <!-- Lombok 简化代码 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

步骤②:配置 RocketMQ

复制代码
rocketmq:
  name-server: localhost:9876
  producer:
    group: springboot-producer
    send-message-timeout: 3000
    compress-message-body-threshold: 4096
    max-message-size: 4194304
    retry-times-when-send-failed: 3
    retry-times-when-send-async-failed: 3
  consumer:
    group: springboot-consumer
    consume-message-max-retries: 3
    consume-thread-min: 5
    consume-thread-max: 20

步骤③:使用RocketMQTemplate操作RocketMQ创建消息发送服务

复制代码
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.apache.rocketmq.spring.support.RocketMQHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

@Service
public class RocketMQProducer {

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    /**
     * 发送同步消息
     */
    public void sendSyncMessage(String topic, String message) {
        SendResult sendResult = rocketMQTemplate.syncSend(topic, message);
        System.out.println("同步消息发送结果: " + sendResult);
    }

    /**
     * 发送异步消息
     */
    public void sendAsyncMessage(String topic, String message) {
        rocketMQTemplate.asyncSend(topic, message, sendResult -> {
            System.out.println("异步消息发送结果: " + sendResult);
        });
    }

    /**
     * 发送单向消息
     */
    public void sendOneWayMessage(String topic, String message) {
        rocketMQTemplate.sendOneWay(topic, message);
        System.out.println("单向消息已发送");
    }

    /**
     * 发送带标签的消息
     */
    public void sendWithTagMessage(String topic, String tag, String message) {
        String destination = topic + ":" + tag;
        SendResult sendResult = rocketMQTemplate.syncSend(destination, message);
        System.out.println("带标签的消息发送结果: " + sendResult);
    }

    /**
     * 发送延迟消息
     */
    public void sendDelayMessage(String topic, String message, int delayLevel) {
        Message<String> msg = MessageBuilder.withPayload(message)
                .build();
        SendResult sendResult = rocketMQTemplate.syncSend(topic, msg, 3000, delayLevel);
        System.out.println("延迟消息发送结果: " + sendResult);
    }

    /**
     * 发送顺序消息
     */
    public void sendOrderlyMessage(String topic, String message, int hashKey) {
        SendResult sendResult = rocketMQTemplate.syncSendOrderly(topic, message, "orderly", hashKey);
        System.out.println("顺序消息发送结果: " + sendResult);
    }

    /**
     * 发送事务消息
     */
    public void sendTransactionMessage(String topic, String message) {
        Message<String> msg = MessageBuilder.withPayload(message)
                .setHeader(RocketMQHeaders.TRANSACTION_ID, "tx-123")
                .build();
        rocketMQTemplate.sendMessageInTransaction(topic, msg, null);
    }

    /**
     * 发送对象消息
     */
    public void sendObjectMessage(String topic, User user) {
        SendResult sendResult = rocketMQTemplate.syncSend(topic, user);
        System.out.println("对象消息发送结果: " + sendResult);
    }

    /**
     * 发送带header的消息
     */
    public void sendWithHeaderMessage(String topic, String message) {
        Map<String, Object> headers = new HashMap<>();
        headers.put("message-type", "test");
        headers.put("sender", "system");
        headers.put("timestamp", System.currentTimeMillis());
        
        Message<String> msg = MessageBuilder.withPayload(message)
                .copyHeaders(headers)
                .build();
        
        SendResult sendResult = rocketMQTemplate.syncSend(topic, msg);
        System.out.println("带header的消息发送结果: " + sendResult);
    }
}

步骤④: 创建消息接收服务

复制代码
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Service;

import java.io.UnsupportedEncodingException;

@Service
public class RocketMQConsumer {

    /**
     * 接收普通消息
     */
    @RocketMQMessageListener(topic = "springboot-topic", consumerGroup = "springboot-consumer")
    public class StringConsumer implements RocketMQListener<String> {
        @Override
        public void onMessage(String message) {
            System.out.println("接收到普通消息: " + message);
        }
    }

    /**
     * 接带标签的消息
     */
    @RocketMQMessageListener(
            topic = "springboot-topic",
            consumerGroup = "springboot-consumer-tag",
        selectorExpression = "tagA || tagB"
    )
    public class TagConsumer implements RocketMQListener<String> {
        @Override
        public void onMessage(String message) {
            System.out.println("接收到带标签的消息: " + message);
        }
    }

    /**
     * 接收顺序消息
     */
    @RocketMQMessageListener(
            topic = "springboot-topic",
            consumerGroup = "springboot-consumer-orderly",
        consumeMode = org.apache.rocketmq.spring.annotation.ConsumeMode.ORDERLY
    )
    public class OrderlyConsumer implements RocketMQListener<String> {
        @Override
        public void onMessage(String message) {
            System.out.println("接收到顺序消息: " + message);
        }
    }

    /**
     * 接收对象消息
     */
    @RocketMQMessageListener(
            topic = "springboot-topic",
            consumerGroup = "springboot-consumer-object"
    )
    public class ObjectConsumer implements RocketMQListener<User> {
        @Override
        public void onMessage(User user) {
            System.out.println("接收到对象消息: " + user);
        }
    }

    /**
     * 接收原始消息(MessageExt)
     */
    @RocketMQMessageListener(
            topic = "springboot-topic",
            consumerGroup = "springboot-consumer-raw"
    )
    public class RawMessageConsumer implements RocketMQListener<MessageExt> {
        @Override
        public void onMessage(MessageExt messageExt) {
            try {
                String message = new String(messageExt.getBody(), "UTF-8");
                System.out.println("接收到原始消息: " + message);
                System.out.println("消息标签: " + messageExt.getTags());
                System.out.println("消息keys: " + messageExt.getKeys());
                System.out.println("消息延迟级别: " + messageExt.getDelayTimeLevel());
                System.out.println("消息重试次数: " + messageExt.getReconsumeTimes());
                
                // 手理确认消息
                // 如果不处理,默认会自动确认
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }
    }
}

springboot整合Kafka

步骤①:添加依赖

复制代码
<dependencies>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- Spring Kafka -->
    <dependency>
        <groupId>org.springframework.kafka</groupId>
        <artifactId>spring-kafka</artifactId>
    </dependency>
    
    <!-- Lombok 简化代码 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

步骤②:配置 Kafka

复制代码
spring:
  kafka:
    bootstrap-servers: localhost:9092
    listener:
      concurrency: 3
      poll-timeout: 3000
    producer:
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.apache.kafka.common.serialization.StringSerializer
    consumer:
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      auto-offset-reset: earliest
      group-id: springboot-group
      enable-auto-commit: true
      auto-commit-interval: 1000
    properties:
      spring:
        json:
          trusted:
            packages: "*"

步骤③:创建 Kafka 配置类

创建一个配置类来设置 Kafka 相关的 Bean:

复制代码
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.common.serialization.StringDeserializer;
import org.apache.kafka.common.serialization.StringSerializer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.annotation.EnableKafka;
import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
import org.springframework.kafka.core.*;
import org.springframework.kafka.listener.ContainerProperties;
import org.springframework.kafka.transaction.KafkaTransactionManager;

import java.util.HashMap;
import java.util.Map;

@Configuration
@EnableKafka
public class KafkaConfig {

    @Value("${spring.kafka.bootstrap-servers}")
    private String bootstrapServers;

    /**
     * 生产者配置
     */
    @Bean
    public ProducerFactory<String, String> producerFactory() {
        Map<String, Object> props = new HashMap<>();
        props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
        props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
        props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
        props.put(ProducerConfig.ACKS_CONFIG, "all");
        props.put(ProducerConfig.RETRIES_CONFIG, 3);
        props.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384);
        props.put(ProducerConfig.LINGER_MS_CONFIG, 1);
        props.put(ProducerConfig.BUFFER_MEMORY_CONFIG, 33554432);
        return new DefaultKafkaProducerFactory<>(props);
    }

    /**
     * Kafka 模板
     */
    @Bean
    public KafkaTemplate<String, String> kafkaTemplate() {
        return new KafkaTemplate<>(producerFactory());
    }

    /**
     * 消费者配置
     */
    @Bean
    public ConsumerFactory<String, String> consumerFactory() {
        Map<String, Object> props = new HashMap<>();
        props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
        props.put(ConsumerConfig.GROUP_ID_CONFIG, "springboot-group");
        props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
        props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
        props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
        props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, true);
        props.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, 1000);
        return new DefaultKafkaConsumerFactory<>(props);
    }

    /**
     * 监听器容器工厂
     */
    @Bean
    public ConcurrentKafkaListenerContainerFactory<String, String> kafkaListenerContainerFactory() {
        ConcurrentKafkaListenerContainerFactory<String, String> factory = 
            new ConcurrentKafkaListenerContainerFactory<>();
        factory.setConsumerFactory(consumerFactory());
        factory.getContainerProperties().setPollTimeout(3000);
        factory.setConcurrency(3);
        return factory;
    }

    /**
     * 事务管理器
     */
    @Bean
    public KafkaTransactionManager<String, String> kafkaTransactionManager() {
        return new KafkaTransactionManager<>(producerFactory());
    }
}

步骤④: 创建 Kafka 生产者服务

创建一个服务类用于发送消息:

复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.support.SendResult;
import org.springframework.stereotype.Service;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureCallback;

import java.util.concurrent.ExecutionException;

@Service
public class KafkaProducer {

    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;

    /**
     * 发送简单消息
     */
    public void send(String topic, String message) {
        kafkaTemplate.send(topic, message);
        System.out.println("发送消息: " + message);
    }

    /**
     * 发送消息并获取发送结果
     */
    public void sendWithCallback(String topic, String message) {
        ListenableFuture<SendResult<String, String>> future = kafkaTemplate.send(topic, message);
        
        future.addCallback(new ListenableFutureCallback<SendResult<String, String>>() {
            @Override
            public void onSuccess(SendResult<String, String> result) {
                System.out.println("发送成功: " + message + ", offset: " + result.getRecordMetadata().offset());
            }

            @Override
            public void onFailure(Throwable ex) {
                System.out.println("发送失败: " + message + ", 原因: " + ex.getMessage());
            }
        });
    }

    /**
     * 发送带key的消息
     */
    public void sendWithKey(String topic, String key, String message) {
        kafkaTemplate.send(topic, key, message);
        System.out.println("发送带key的消息: " + key + ", " + message);
    }

    /**
     * 发送消息并等待结果
     */
    public void sendAndWait(String topic, String message) throws ExecutionException, InterruptedException {
        SendResult<String, String> result = kafkaTemplate.send(topic, message).get();
        System.out.println("发送消息: " + message + ", offset: " + result.getRecordMetadata().offset());
    }

    /**
     * 发送事务消息
     */
    @org.springframework.transaction.annotation.Transactional
    public void sendInTransaction(String topic, String message) {
        kafkaTemplate.executeInTransaction(template -> {
            template.send(topic, message);
            System.out.println("发送事务消息: " + message);
            return true;
        });
    }
}

步骤⑤:创建 Kafka 消费者服务

创建一个服务类用于接收消息:

复制代码
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.kafka.annotation.KafkaListeners;
import org.springframework.kafka.annotation.TopicPartition;
import org.springframework.kafka.support.KafkaHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.stereotype.Service;

@Service
public class KafkaConsumer {

    /**
     * 监听单个主题
     */
    @KafkaListener(topics = "springboot-topic")
    public void listen(String message) {
        System.out.println("接收到消息: " + message);
    }

    /**
     * 监听多个主题
     */
    @KafkaListeners({
        @KafkaListener(topics = "topic1"),
        @KafkaListener(topics = "topic2")
    })
    public void listenMultipleTopics(String message) {
        System.out.println("接收到消息: " + message);
    }

    /**
     * 监听特定分区的消息
     */
    @KafkaListener(topicPartitions = @TopicPartition(
        topic = "springboot-topic",
        partitions = "0"
    ))
    public void listenPartition(String message) {
        System.out.println("接收到分区0的消息: " + message);
    }

    /**
     * 监听多个分区
     */
    @KafkaListener(topicPartitions = @TopicPartition(
        topic = "springboot-topic",
        partitions = {"0", "1"}
    ))
    public void listenMultiplePartitions(String message) {
        System.out.println("接收到分区消息: " + message);
    }

    /**
     * 获取消息元数据
     */
    @KafkaListener(topics = "springboot-topic")
    public void listenWithHeaders(
        @Payload String message,
        @Header(KafkaHeaders.RECEIVED_TOPIC) String topic,
        @Header(KafkaHeaders.RECEIVED_PARTITION_ID) int partition,
        @Header(KafkaHeaders.OFFSET) long offset,
        @Header(KafkaHeaders.RECEIVED_TIMESTAMP) long timestamp
    ) {
        System.out.println("接收到消息: " + message);
        System.out.println("主题: " + topic);
        System.out.println("分区: " + partition);
        System.out.println("偏移量: " + offset);
        System.out.println("时间戳: " + timestamp);
    }

    /**
     * 手动提交偏移量
     */
    @KafkaListener(
        topics = "springboot-topic",
        containerFactory = "kafkaManualAckListenerContainerFactory"
    )
    public void listenManualAck(String message) {
        System.out.println("接收到消息: " + message);
        
        // 处理消息后手动提交偏移量
        // 需要配置监听器容器工厂为手动提交模式
    }
}

监控

Spring Boot Admin

介绍

SpringBootAdmin是一个管理和监控SpringBoot应用程序的开源项目。它提供了一个美观的WebUI,用于管理和监控SpringBoot应用程序的各种运行状态指标,如健康状态、内存使用、JVM线程、环境变量、配置属性等。

架构

Spring Boot Admin 采用客户端-服务器架构:

  • Spring Boot Admin Server:中央服务器,负责收集和展示所有客户端应用的信息

  • Spring Boot Admin Client:需要监控的应用程序,通过 HTTP 向服务器注册自己

部署 Spring Boot Admin Server

步骤①:添加依赖

复制代码
<dependencies>
    <!-- Spring Boot Admin Server -->
    <dependency>
        <groupId>de.codecentric</groupId>
        <artifactId>spring-boot-admin-starter-server</artifactId>
        <version>2.6.6</version>
    </dependency>
    
    <!-- Spring Boot Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- Spring Boot Security (可选,用于认证) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
</dependencies>

or

步骤②: 启用 Spring Boot Admin Server

复制代码
import de.codecentric.boot.admin.server.config.EnableAdminServer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

步骤③: 配置 Spring Boot Admin Server

application.ymlapplication.properties 文件中配置 Spring Boot Admin Server:

复制代码
server:
  port: 8080

spring:
  boot:
    admin:
      ui:
        title: "Spring Boot Admin"
      discovery:
        enabled: true
      context-path: /admin

启动应用后,访问 http://localhost:8080/admin 即可看到 Spring Boot Admin Server 的界面。

部署 Spring Boot Admin Client

步骤①:添加依赖

复制代码
<dependencies>
    <!-- Spring Boot Admin Client -->
    <dependency>
        <groupId>de.codecentric</groupId>
        <artifactId>spring-boot-admin-starter-client</artifactId>
        <version>2.6.6</version>
    </dependency>
    
    <!-- Spring Boot Actuator -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
</dependencies>

步骤②: 配置 Spring Boot Admin Client

application.ymlapplication.properties 文件中配置 Spring Boot Admin Client:

复制代码
spring:
  boot:
    admin:
      client:
        url: http://localhost:8080/admin
        instance:
          name: my-application
          prefer-ip: true
  application:
    name: my-application

management:
  endpoints:
    web:
      exposure:
        include: "*"
  endpoint:
    health:
      show-details: always

启动应用

启动应用后,Spring Boot Admin Client 会自动向 Spring Boot Admin Server 注册自己。在 Admin Server 的界面上,可以看到该应用程序的状态和各项指标。

监控原理

Spring Boot Admin 依赖于 Spring Boot Actuator 来收集和展示应用程序的各种指标和状态信息。Actuator 是 Spring Boot 的一个子项目,它提供了生产级别的监控和管理功能。Spring Boot Admin 通过与 Actuator 集成,将这些监控信息以可视化的方式展示在 Web UI 上。

相关推荐
你不是我我13 小时前
【Java 开发日记】HTTP3 性能更好,为什么内网微服务依然多用 HTTP2?HTTP2 内网优势是什么?
java·开发语言·微服务
AI机器学习算法13 小时前
《动手学深度学习PyTorch版》笔记
人工智能·学习·机器学习
雪碧聊技术13 小时前
大模型爆火!Java后端如何抓住Agent全栈开发的风口
java·大模型·agent·全栈开发
贺一航【Niki】13 小时前
【学习笔记】杂乱知识
笔记·学习
Filwaod14 小时前
互联网大厂Java面试实战:Spring Boot微服务架构与AI技术栈深度解析
spring boot·微服务·大厂面试·java面试·技术干货·ai技术栈·程序员求职
白雪茫茫14 小时前
监督学习、半监督学习、无监督学习算法详解
python·学习·算法·ai
逻辑驱动的ken15 小时前
Java高频面试场景题25
java·开发语言·深度学习·面试·职场和发展
AI人工智能+电脑小能手16 小时前
【大白话说Java面试题】【Java基础篇】第32题:Java的异常处理机制是什么
java·开发语言·后端·面试
ltl16 小时前
Softmax 与概率分布:从分数到选择的桥
后端
刀法如飞16 小时前
Claude Code Skills 推荐:2026年最值得安装的10个AI技能
前端·后端·ai编程