最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring Boot 整合第三方組件Redis、MyBatis、Kafka 實(shí)戰(zhàn)案例指南

 更新時(shí)間:2025年10月31日 11:55:41   作者:湮酒  
Spring Boot通過Starter機(jī)制和自動(dòng)裝配簡(jiǎn)化了第三方組件整合,本文以Redis為例,展示了Spring Boot的整合優(yōu)勢(shì),本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧

?? Spring Boot 整合第三方組件:Redis、MyBatis、Kafka 實(shí)戰(zhàn)

?? 一、Spring Boot Starter 設(shè)計(jì)哲學(xué)

?? 約定優(yōu)于配置的核心思想

??傳統(tǒng)整合 vs Spring Boot Starter 對(duì)比??:

方面傳統(tǒng)方式Spring Boot Starter優(yōu)勢(shì) / 成果
依賴管理手動(dòng)維護(hù)版本、易沖突起步依賴(Starter)自動(dòng)聚合與版本對(duì)齊版本沖突減少 80%,依賴升級(jí)更安全
配置復(fù)雜度需大量 XML 或 Java 配置自動(dòng)配置 + 合理默認(rèn)值(約定優(yōu)于配置)配置代碼減少 70%,開發(fā)效率提升
啟動(dòng)速度手動(dòng)加載配置,啟動(dòng)較慢條件化自動(dòng)裝配,按需加載模塊啟動(dòng)時(shí)間縮短 50%,資源占用更優(yōu)
維護(hù)成本配置分散,項(xiàng)目間不一致統(tǒng)一 Starter 標(biāo)準(zhǔn)配置與依賴管理維護(hù)效率提升 60%,團(tuán)隊(duì)協(xié)作更順暢

?? 自動(dòng)裝配機(jī)制揭秘

??Spring Boot 自動(dòng)配置流程??:

graph TB
    A[啟動(dòng)類] --> B[@SpringBootApplication]
    B --> C[@EnableAutoConfiguration]
    C --> D[spring.factories]
    D --> E[自動(dòng)配置類]
    E --> F[條件注解檢查]
    F --> G[創(chuàng)建Bean]
    G --> H[完成整合]
    style E fill:#bbdefb,stroke:#333
    style F fill:#c8e6c9,stroke:#333

??條件注解工作原理示例??:

@Configuration
@ConditionalOnClass(RedisTemplate.class)  // 類路徑存在時(shí)生效
@ConditionalOnProperty(prefix = "spring.redis", name = "enabled", matchIfMissing = true)
@EnableConfigurationProperties(RedisProperties.class)  // 綁定配置
public class RedisAutoConfiguration {
    @Bean
    @ConditionalOnMissingBean  // 容器中不存在時(shí)創(chuàng)建
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        return template;
    }
}

?? 二、Redis 整合實(shí)戰(zhàn)

?? 依賴配置與自動(dòng)裝配

??Maven 依賴配置??:

<!-- pom.xml -->
<dependencies>
    <!-- Redis Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <!-- 連接池 -->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
    </dependency>
    <!-- JSON 序列化 -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>
</dependencies>

??application.yml 配置??:

# application.yml
spring:
  redis:
    # 連接配置
    host: localhost
    port: 6379
    password: 123456
    database: 0
    timeout: 2000ms
    # 連接池配置
    lettuce:
      pool:
        max-active: 20      # 最大連接數(shù)
        max-idle: 10       # 最大空閑連接
        min-idle: 5        # 最小空閑連接
        max-wait: 1000ms   # 獲取連接最大等待時(shí)間
    # 集群配置(可選)
    # cluster:
    #   nodes: 
    #     - 192.168.1.101:6379
    #     - 192.168.1.102:6379

?? Redis 配置類

??自定義 Redis 配置??:

@Configuration
@Slf4j
public class RedisConfig {
    @Autowired
    private RedisConnectionFactory redisConnectionFactory;
    /**
     * 自定義 RedisTemplate,解決序列化問題
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        // 使用 Jackson2JsonRedisSerializer 替換默認(rèn)序列化
        Jackson2JsonRedisSerializer<Object> serializer = 
            new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.activateDefaultTyping(
            LazyIterator.class, ObjectMapper.DefaultTyping.NON_FINAL);
        serializer.setObjectMapper(objectMapper);
        // 設(shè)置 key 和 value 的序列化規(guī)則
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(serializer);
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(serializer);
        template.afterPropertiesSet();
        log.info("RedisTemplate 配置完成");
        return template;
    }
    /**
     * 緩存管理器配置
     */
    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory factory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofMinutes(30))  // 默認(rèn)緩存30分鐘
            .disableCachingNullValues()         // 不緩存null值
            .serializeKeysWith(RedisSerializationContext.SerializationPair
                .fromSerializer(new StringRedisSerializer()))
            .serializeValuesWith(RedisSerializationContext.SerializationPair
                .fromSerializer(new GenericJackson2JsonRedisSerializer()));
        return RedisCacheManager.builder(factory)
            .cacheDefaults(config)
            .build();
    }
}

?? Redis 操作實(shí)戰(zhàn)

??基礎(chǔ)數(shù)據(jù)操作服務(wù)??:

@Service
@Slf4j
public class RedisOperationService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    /**
     * 字符串操作
     */
    public void stringOperations() {
        // 1. 設(shè)置值
        redisTemplate.opsForValue().set("user:1001:name", "張三");
        redisTemplate.opsForValue().set("user:1001:age", 25, Duration.ofMinutes(30));
        // 2. 獲取值
        String name = (String) redisTemplate.opsForValue().get("user:1001:name");
        Integer age = (Integer) redisTemplate.opsForValue().get("user:1001:age");
        log.info("用戶信息 - 姓名: {}, 年齡: {}", name, age);
        // 3. 原子操作
        Long increment = redisTemplate.opsForValue().increment("counter", 1);
        log.info("計(jì)數(shù)器值: {}", increment);
    }
    /**
     * Hash 操作
     */
    public void hashOperations() {
        String key = "user:1001:profile";
        // 1. 設(shè)置Hash字段
        Map<String, Object> userMap = new HashMap<>();
        userMap.put("name", "李四");
        userMap.put("age", 30);
        userMap.put("email", "lisi@example.com");
        redisTemplate.opsForHash().putAll(key, userMap);
        // 2. 獲取單個(gè)字段
        String name = (String) redisTemplate.opsForHash().get(key, "name");
        log.info("Hash字段 name: {}", name);
        // 3. 獲取所有字段
        Map<Object, Object> entries = redisTemplate.opsForHash().entries(key);
        log.info("完整用戶信息: {}", entries);
    }
    /**
     * List 操作
     */
    public void listOperations() {
        String key = "recent:users";
        // 1. 從左端插入
        redisTemplate.opsForList().leftPush(key, "user1");
        redisTemplate.opsForList().leftPush(key, "user2");
        redisTemplate.opsForList().leftPush(key, "user3");
        // 2. 獲取范圍
        List<Object> recentUsers = redisTemplate.opsForList().range(key, 0, 2);
        log.info("最近訪問用戶: {}", recentUsers);
    }
    /**
     * Set 操作
     */
    public void setOperations() {
        String key = "user:tags:1001";
        // 1. 添加元素
        redisTemplate.opsForSet().add(key, "vip", "active", "new");
        // 2. 判斷元素是否存在
        Boolean isVip = redisTemplate.opsForSet().isMember(key, "vip");
        log.info("是否是VIP: {}", isVip);
        // 3. 獲取所有元素
        Set<Object> tags = redisTemplate.opsForSet().members(key);
        log.info("用戶標(biāo)簽: {}", tags);
    }
}

??緩存注解實(shí)戰(zhàn)??:

@Service
@Slf4j
public class UserService {
    /**
     * 緩存用戶信息
     */
    @Cacheable(value = "users", key = "#userId", unless = "#result == null")
    public User getUserById(Long userId) {
        log.info("查詢數(shù)據(jù)庫獲取用戶: {}", userId);
        // 模擬數(shù)據(jù)庫查詢
        return findUserFromDB(userId);
    }
    /**
     * 更新緩存
     */
    @CachePut(value = "users", key = "#user.id")
    public User updateUser(User user) {
        log.info("更新用戶信息: {}", user.getId());
        // 模擬數(shù)據(jù)庫更新
        return updateUserInDB(user);
    }
    /**
     * 刪除緩存
     */
    @CacheEvict(value = "users", key = "#userId")
    public void deleteUser(Long userId) {
        log.info("刪除用戶: {}", userId);
        // 模擬數(shù)據(jù)庫刪除
        deleteUserFromDB(userId);
    }
    /**
     * 復(fù)雜緩存配置
     */
    @Caching(
        cacheable = {
            @Cacheable(value = "user_detail", key = "#userId")
        },
        put = {
            @CachePut(value = "user_profile", key = "#userId")
        }
    )
    public User getUserDetail(Long userId) {
        log.info("獲取用戶詳情: {}", userId);
        return findUserDetailFromDB(userId);
    }
    // 模擬數(shù)據(jù)庫操作
    private User findUserFromDB(Long userId) {
        // 實(shí)際項(xiàng)目中這里會(huì)查詢數(shù)據(jù)庫
        return new User(userId, "用戶" + userId, "user" + userId + "@example.com");
    }
    private User updateUserInDB(User user) {
        return user;
    }
    private void deleteUserFromDB(Long userId) {
        // 刪除操作
    }
    private User findUserDetailFromDB(Long userId) {
        return new User(userId, "詳情用戶" + userId, "detail" + userId + "@example.com");
    }
    @Data
    @AllArgsConstructor
    public static class User {
        private Long id;
        private String name;
        private String email;
    }
}

?? Redis 健康檢查與監(jiān)控

??Redis 健康指示器??:

@Component
public class RedisHealthIndicator implements HealthIndicator {
    @Autowired
    private RedisConnectionFactory redisConnectionFactory;
    @Override
    public Health health() {
        try {
            // 測(cè)試Redis連接
            RedisConnection connection = redisConnectionFactory.getConnection();
            try {
                String result = connection.ping();
                if ("PONG".equals(result)) {
                    return Health.up()
                        .withDetail("server", "redis")
                        .withDetail("version", getRedisVersion(connection))
                        .withDetail("status", "connected")
                        .build();
                } else {
                    return Health.down()
                        .withDetail("error", "Unexpected ping response: " + result)
                        .build();
                }
            } finally {
                connection.close();
            }
        } catch (Exception e) {
            return Health.down(e)
                .withDetail("error", "Redis connection failed: " + e.getMessage())
                .build();
        }
    }
    private String getRedisVersion(RedisConnection connection) {
        try {
            Properties info = connection.info("server");
            return info.getProperty("redis_version");
        } catch (Exception e) {
            return "unknown";
        }
    }
}

??? 三、MyBatis 整合實(shí)戰(zhàn)

?? MyBatis 依賴配置

??Maven 依賴??:

<!-- pom.xml -->
<dependencies>
    <!-- MyBatis Starter -->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.2.2</version>
    </dependency>
    <!-- MySQL 驅(qū)動(dòng) -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    <!-- 數(shù)據(jù)源 -->
    <dependency>
        <groupId>com.zaxxer</groupId>
        <artifactId>HikariCP</artifactId>
    </dependency>
</dependencies>

??數(shù)據(jù)庫配置??:

# application.yml
spring:
  datasource:
    # 數(shù)據(jù)源配置
    url: jdbc:mysql://localhost:3306/spring_boot_demo?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
    # Hikari 連接池配置
    hikari:
      maximum-pool-size: 20
      minimum-idle: 5
      connection-timeout: 30000
      idle-timeout: 300000
      max-lifetime: 1200000
# MyBatis 配置
mybatis:
  #  mapper 文件位置
  mapper-locations: classpath:mapper/*.xml
  # 別名掃描包
  type-aliases-package: com.example.entity
  # 開啟駝峰命名轉(zhuǎn)換
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  # 全局配置
  global-config:
    db-config:
      id-type: auto
      logic-delete-field: deleted  # 邏輯刪除字段
      logic-delete-value: 1        # 邏輯已刪除值
      logic-not-delete-value: 0    # 邏輯未刪除值

??? 數(shù)據(jù)層架構(gòu)設(shè)計(jì)

??實(shí)體類定義??:

@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("user")  // MyBatis-Plus 注解,標(biāo)準(zhǔn) MyBatis 可省略
public class User {
    @TableId(type = IdType.AUTO)  // 主鍵自增
    private Long id;
    private String name;
    private String email;
    private Integer age;
    @TableField(fill = FieldFill.INSERT)  // 插入時(shí)自動(dòng)填充
    private LocalDateTime createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)  // 插入和更新時(shí)填充
    private LocalDateTime updateTime;
    @TableLogic  // 邏輯刪除標(biāo)識(shí)
    private Integer deleted;
}

??Mapper 接口定義??:

@Mapper
@Repository
public interface UserMapper {
    /**
     * 根據(jù)ID查詢用戶
     */
    @Select("SELECT * FROM user WHERE id = #{id} AND deleted = 0")
    User selectById(Long id);
    /**
     * 查詢所有用戶
     */
    @Select("SELECT * FROM user WHERE deleted = 0 ORDER BY create_time DESC")
    List<User> selectAll();
    /**
     * 插入用戶
     */
    @Insert("INSERT INTO user(name, email, age, create_time, update_time) " +
            "VALUES(#{name}, #{email}, #{age}, NOW(), NOW())")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    int insert(User user);
    /**
     * 更新用戶
     */
    @Update("UPDATE user SET name=#{name}, email=#{email}, age=#{age}, " +
            "update_time=NOW() WHERE id=#{id} AND deleted = 0")
    int update(User user);
    /**
     * 邏輯刪除用戶
     */
    @Update("UPDATE user SET deleted=1, update_time=NOW() WHERE id=#{id}")
    int deleteById(Long id);
    /**
     * 根據(jù)郵箱查詢用戶
     */
    @Select("SELECT * FROM user WHERE email = #{email} AND deleted = 0")
    User selectByEmail(String email);
    /**
     * 分頁查詢用戶
     */
    @Select("SELECT * FROM user WHERE deleted = 0 ORDER BY create_time DESC " +
            "LIMIT #{offset}, #{pageSize}")
    List<User> selectByPage(@Param("offset") int offset, 
                           @Param("pageSize") int pageSize);
}

??XML Mapper 配置??:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UserMapper">
    <!-- 自定義結(jié)果映射 -->
    <resultMap id="UserResultMap" type="User">
        <id column="id" property="id" />
        <result column="name" property="name" />
        <result column="email" property="email" />
        <result column="age" property="age" />
        <result column="create_time" property="createTime" />
        <result column="update_time" property="updateTime" />
        <result column="deleted" property="deleted" />
    </resultMap>
    <!-- 復(fù)雜查詢:根據(jù)條件動(dòng)態(tài)查詢用戶 -->
    <select id="selectByCondition" parameterType="map" resultMap="UserResultMap">
        SELECT * FROM user 
        WHERE deleted = 0
        <if test="name != null and name != ''">
            AND name LIKE CONCAT('%', #{name}, '%')
        </if>
        <if test="minAge != null">
            AND age >= #{minAge}
        </if>
        <if test="maxAge != null">
            AND age <= #{maxAge}
        </if>
        <if test="email != null and email != ''">
            AND email = #{email}
        </if>
        ORDER BY create_time DESC
    </select>
    <!-- 批量插入用戶 -->
    <insert id="batchInsert" parameterType="list">
        INSERT INTO user (name, email, age, create_time, update_time) 
        VALUES
        <foreach collection="list" item="user" separator=",">
            (#{user.name}, #{user.email}, #{user.age}, NOW(), NOW())
        </foreach>
    </insert>
    <!-- 統(tǒng)計(jì)用戶數(shù)量 -->
    <select id="countUsers" resultType="int">
        SELECT COUNT(*) FROM user WHERE deleted = 0
    </select>
</mapper>

?? Service 層實(shí)現(xiàn)

??業(yè)務(wù)服務(wù)類??:

@Service
@Slf4j
@Transactional
public class UserService {
    @Autowired
    private UserMapper userMapper;
    /**
     * 根據(jù)ID查詢用戶
     */
    public User getUserById(Long id) {
        log.info("查詢用戶: {}", id);
        User user = userMapper.selectById(id);
        if (user == null) {
            throw new RuntimeException("用戶不存在");
        }
        return user;
    }
    /**
     * 查詢所有用戶
     */
    public List<User> getAllUsers() {
        log.info("查詢所有用戶");
        return userMapper.selectAll();
    }
    /**
     * 創(chuàng)建用戶
     */
    public User createUser(User user) {
        log.info("創(chuàng)建用戶: {}", user.getName());
        // 檢查郵箱是否已存在
        User existingUser = userMapper.selectByEmail(user.getEmail());
        if (existingUser != null) {
            throw new RuntimeException("郵箱已存在");
        }
        int result = userMapper.insert(user);
        if (result > 0) {
            return user;
        } else {
            throw new RuntimeException("創(chuàng)建用戶失敗");
        }
    }
    /**
     * 更新用戶
     */
    public User updateUser(User user) {
        log.info("更新用戶: {}", user.getId());
        // 檢查用戶是否存在
        User existingUser = userMapper.selectById(user.getId());
        if (existingUser == null) {
            throw new RuntimeException("用戶不存在");
        }
        int result = userMapper.update(user);
        if (result > 0) {
            return getUserById(user.getId());
        } else {
            throw new RuntimeException("更新用戶失敗");
        }
    }
    /**
     * 刪除用戶(邏輯刪除)
     */
    public void deleteUser(Long id) {
        log.info("刪除用戶: {}", id);
        User existingUser = userMapper.selectById(id);
        if (existingUser == null) {
            throw new RuntimeException("用戶不存在");
        }
        int result = userMapper.deleteById(id);
        if (result == 0) {
            throw new RuntimeException("刪除用戶失敗");
        }
    }
    /**
     * 分頁查詢用戶
     */
    public PageResult<User> getUsersByPage(int pageNum, int pageSize) {
        log.info("分頁查詢用戶: 頁碼={}, 大小={}", pageNum, pageSize);
        int offset = (pageNum - 1) * pageSize;
        List<User> users = userMapper.selectByPage(offset, pageSize);
        int total = userMapper.countUsers();
        return new PageResult<>(users, total, pageNum, pageSize);
    }
    /**
     * 條件查詢用戶
     */
    public List<User> getUsersByCondition(String name, Integer minAge, Integer maxAge, String email) {
        log.info("條件查詢用戶: name={}, minAge={}, maxAge={}, email={}", 
                 name, minAge, maxAge, email);
        Map<String, Object> params = new HashMap<>();
        params.put("name", name);
        params.put("minAge", minAge);
        params.put("maxAge", maxAge);
        params.put("email", email);
        return userMapper.selectByCondition(params);
    }
    /**
     * 分頁結(jié)果封裝類
     */
    @Data
    @AllArgsConstructor
    public static class PageResult<T> {
        private List<T> data;
        private long total;
        private int pageNum;
        private int pageSize;
        private int totalPages;
        public PageResult(List<T> data, long total, int pageNum, int pageSize) {
            this.data = data;
            this.total = total;
            this.pageNum = pageNum;
            this.pageSize = pageSize;
            this.totalPages = (int) Math.ceil((double) total / pageSize);
        }
    }
}

?? MyBatis 配置優(yōu)化

??MyBatis 配置類??:

@Configuration
@MapperScan("com.example.mapper")  // 掃描Mapper接口
@Slf4j
public class MyBatisConfig {
    /**
     * 配置MyBatis SqlSessionFactory
     */
    @Bean
    @ConfigurationProperties(prefix = "mybatis.configuration")
    public org.apache.ibatis.session.Configuration globalConfiguration() {
        org.apache.ibatis.session.Configuration configuration = 
            new org.apache.ibatis.session.Configuration();
        configuration.setMapUnderscoreToCamelCase(true);
        configuration.setLogImpl(StdOutImpl.class);
        configuration.setCacheEnabled(true);
        return configuration;
    }
    /**
     * 分頁插件配置
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 分頁插件
        PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor();
        paginationInterceptor.setMaxLimit(1000L);  // 單頁最大記錄數(shù)
        paginationInterceptor.setOverflow(true);   // 超過最大頁數(shù)時(shí)返回第一頁
        interceptor.addInnerInterceptor(paginationInterceptor);
        // 樂觀鎖插件(可選)
        OptimisticLockerInnerInterceptor optimisticLockerInterceptor = 
            new OptimisticLockerInnerInterceptor();
        interceptor.addInnerInterceptor(optimisticLockerInterceptor);
        log.info("MyBatis 插件配置完成");
        return interceptor;
    }
    /**
     * 元對(duì)象處理器(自動(dòng)填充字段)
     */
    @Bean
    public MetaObjectHandler metaObjectHandler() {
        return new MetaObjectHandler() {
            @Override
            public void insertFill(MetaObject metaObject) {
                this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
                this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
            }
            @Override
            public void updateFill(MetaObject metaObject) {
                this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
            }
        };
    }
}

?? 四、Kafka 整合實(shí)戰(zhàn)

?? Kafka 依賴配置

??Maven 依賴??:

<!-- pom.xml -->
<dependencies>
    <!-- Kafka Starter -->
    <dependency>
        <groupId>org.springframework.kafka</groupId>
        <artifactId>spring-kafka</artifactId>
    </dependency>
    <!-- JSON 支持 -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>
</dependencies>

??Kafka 配置??:

# application.yml
spring:
  kafka:
    # Kafka 服務(wù)器配置
    bootstrap-servers: localhost:9092
    # 生產(chǎn)者配置
    producer:
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
      acks: all  # 所有副本確認(rèn)
      retries: 3  # 重試次數(shù)
      batch-size: 16384  # 批量大小
      buffer-memory: 33554432  # 緩沖區(qū)大小
      properties:
        linger.ms: 10  # 發(fā)送延遲
    # 消費(fèi)者配置
    consumer:
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
      group-id: spring-boot-demo-group  # 消費(fèi)者組
      auto-offset-reset: earliest  # 偏移量重置策略
      enable-auto-commit: false  # 手動(dòng)提交偏移量
      properties:
        spring.json.trusted.packages: "*"  # 信任所有包進(jìn)行反序列化
    # 監(jiān)聽器配置
    listener:
      ack-mode: manual  # 手動(dòng)提交模式
      concurrency: 3    # 并發(fā)消費(fèi)者數(shù)量

?? Kafka 配置類

??Kafka 高級(jí)配置??:

@Configuration
@Slf4j
@EnableKafka
public class KafkaConfig {
    @Value("${spring.kafka.bootstrap-servers}")
    private String bootstrapServers;
    /**
     * 生產(chǎn)者工廠配置
     */
    @Bean
    public ProducerFactory<String, Object> 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, JsonSerializer.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, 10);
        props.put(ProducerConfig.BUFFER_MEMORY_CONFIG, 33554432);
        return new DefaultKafkaProducerFactory<>(props);
    }
    /**
     * Kafka 模板
     */
    @Bean
    public KafkaTemplate<String, Object> kafkaTemplate() {
        KafkaTemplate<String, Object> template = new KafkaTemplate<>(producerFactory());
        template.setProducerListener(new ProducerListener<String, Object>() {
            @Override
            public void onSuccess(ProducerRecord<String, Object> record, RecordMetadata metadata) {
                log.info("消息發(fā)送成功: topic={}, partition={}, offset={}", 
                    record.topic(), metadata.partition(), metadata.offset());
            }
            @Override
            public void onError(ProducerRecord<String, Object> record, Exception exception) {
                log.error("消息發(fā)送失敗: topic={}, key={}", record.topic(), record.key(), exception);
            }
        });
        return template;
    }
    /**
     * 消費(fèi)者工廠配置
     */
    @Bean
    public ConsumerFactory<String, Object> consumerFactory() {
        Map<String, Object> props = new HashMap<>();
        props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
        props.put(ConsumerConfig.GROUP_ID_CONFIG, "spring-boot-demo-group");
        props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
        props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, JsonDeserializer.class);
        props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
        props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false);
        props.put(JsonDeserializer.TRUSTED_PACKAGES, "*");
        return new DefaultKafkaConsumerFactory<>(props);
    }
    /**
     * 監(jiān)聽器容器工廠
     */
    @Bean
    public ConcurrentKafkaListenerContainerFactory<String, Object> kafkaListenerContainerFactory() {
        ConcurrentKafkaListenerContainerFactory<String, Object> factory = 
            new ConcurrentKafkaListenerContainerFactory<>();
        factory.setConsumerFactory(consumerFactory());
        factory.setConcurrency(3);  // 并發(fā)消費(fèi)者數(shù)量
        factory.getContainerProperties().setAckMode(ContainerProperties.AckMode.MANUAL);
        // 錯(cuò)誤處理
        factory.setErrorHandler(((thrownException, data) -> {
            log.error("消費(fèi)消息失敗: {}", data, thrownException);
        }));
        return factory;
    }
}

?? 消息生產(chǎn)者實(shí)現(xiàn)

??消息生產(chǎn)服務(wù)??:

@Service
@Slf4j
public class KafkaProducerService {
    @Autowired
    private KafkaTemplate<String, Object> kafkaTemplate;
    /**
     * 發(fā)送用戶注冊(cè)消息
     */
    public void sendUserRegistration(User user) {
        String topic = "user-registration";
        String key = "user_" + user.getId();
        UserRegistrationEvent event = new UserRegistrationEvent(
            user.getId(), user.getName(), user.getEmail(), LocalDateTime.now());
        // 發(fā)送消息
        CompletableFuture<SendResult<String, Object>> future = 
            kafkaTemplate.send(topic, key, event);
        // 異步處理發(fā)送結(jié)果
        future.whenComplete((result, exception) -> {
            if (exception != null) {
                log.error("發(fā)送用戶注冊(cè)消息失敗: userId={}", user.getId(), exception);
            } else {
                log.info("用戶注冊(cè)消息發(fā)送成功: userId={}, offset={}", 
                    user.getId(), result.getRecordMetadata().offset());
            }
        });
    }
    /**
     * 發(fā)送訂單創(chuàng)建消息
     */
    public void sendOrderCreation(Order order) {
        String topic = "order-creation";
        String key = "order_" + order.getId();
        // 確保消息順序:使用訂單ID作為key確保同一訂單的消息發(fā)送到同一分區(qū)
        kafkaTemplate.send(topic, key, order)
            .addCallback(
                result -> log.info("訂單創(chuàng)建消息發(fā)送成功: orderId={}", order.getId()),
                exception -> log.error("訂單創(chuàng)建消息發(fā)送失敗: orderId={}", order.getId(), exception)
            );
    }
    /**
     * 發(fā)送帶事務(wù)的消息
     */
    @Transactional
    public void sendTransactionalMessage(String topic, String key, Object message) {
        kafkaTemplate.executeInTransaction(operations -> {
            operations.send(topic, key, message);
            // 這里可以添加數(shù)據(jù)庫操作,確保消息和數(shù)據(jù)庫操作在同一個(gè)事務(wù)中
            return null;
        });
    }
    /**
     * 用戶注冊(cè)事件
     */
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class UserRegistrationEvent {
        private Long userId;
        private String username;
        private String email;
        private LocalDateTime registerTime;
    }
    /**
     * 訂單實(shí)體
     */
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class Order {
        private Long id;
        private Long userId;
        private BigDecimal amount;
        private LocalDateTime createTime;
    }
}

?? 消息消費(fèi)者實(shí)現(xiàn)

??消息消費(fèi)服務(wù)??:

@Service
@Slf4j
public class KafkaConsumerService {
    @Autowired
    private EmailService emailService;
    @Autowired
    private UserService userService;
    /**
     * 消費(fèi)用戶注冊(cè)消息
     */
    @KafkaListener(topics = "user-registration", groupId = "user-service")
    public void consumeUserRegistration(
            @Payload KafkaProducerService.UserRegistrationEvent event,
            @Header(KafkaHeaders.RECEIVED_KEY) String key,
            @Header(KafkaHeaders.RECEIVED_PARTITION) int partition,
            Acknowledgment ack) {
        log.info("收到用戶注冊(cè)消息: key={}, partition={}, event={}", key, partition, event);
        try {
            // 1. 發(fā)送歡迎郵件
            emailService.sendWelcomeEmail(event.getEmail(), event.getUsername());
            // 2. 初始化用戶積分
            userService.initUserPoints(event.getUserId());
            // 3. 記錄注冊(cè)日志
            log.info("用戶注冊(cè)處理完成: userId={}", event.getUserId());
            // 4. 手動(dòng)提交偏移量
            ack.acknowledge();
        } catch (Exception e) {
            log.error("處理用戶注冊(cè)消息失敗: userId={}", event.getUserId(), e);
            // 根據(jù)業(yè)務(wù)需求決定是否重試
        }
    }
    /**
     * 消費(fèi)訂單創(chuàng)建消息
     */
    @KafkaListener(topics = "order-creation", groupId = "order-service")
    public void consumeOrderCreation(
            @Payload KafkaProducerService.Order order,
            @Header(KafkaHeaders.RECEIVED_TIMESTAMP) long timestamp,
            Acknowledgment ack) {
        log.info("收到訂單創(chuàng)建消息: orderId={}, amount={}, time={}", 
            order.getId(), order.getAmount(), 
            Instant.ofEpochMilli(timestamp).atZone(ZoneId.systemDefault()).toLocalDateTime());
        try {
            // 1. 庫存檢查
            checkInventory(order);
            // 2. 風(fēng)控檢查
            riskControlCheck(order);
            // 3. 發(fā)送訂單確認(rèn)
            sendOrderConfirmation(order);
            log.info("訂單處理完成: orderId={}", order.getId());
            ack.acknowledge();
        } catch (Exception e) {
            log.error("處理訂單消息失敗: orderId={}", order.getId(), e);
            // 根據(jù)異常類型決定處理策略
            if (e instanceof InventoryException) {
                // 庫存不足,需要人工處理
                handleInventoryShortage(order, e);
            } else {
                // 其他異常,可以重試
                throw e;
            }
        }
    }
    /**
     * 批量消費(fèi)消息
     */
    @KafkaListener(topics = "batch-processing", groupId = "batch-service")
    public void consumeBatchMessages(
            List<ConsumerRecord<String, Object>> records,
            Acknowledgment ack) {
        log.info("收到批量消息,數(shù)量: {}", records.size());
        try {
            for (ConsumerRecord<String, Object> record : records) {
                processSingleRecord(record);
            }
            // 批量提交偏移量
            ack.acknowledge();
        } catch (Exception e) {
            log.error("批量處理消息失敗", e);
            // 可以根據(jù)業(yè)務(wù)需求實(shí)現(xiàn)重試邏輯
        }
    }
    // 模擬業(yè)務(wù)方法
    private void checkInventory(KafkaProducerService.Order order) {
        // 庫存檢查邏輯
        log.info("檢查庫存: orderId={}", order.getId());
    }
    private void riskControlCheck(KafkaProducerService.Order order) {
        // 風(fēng)控檢查邏輯
        log.info("風(fēng)控檢查: orderId={}", order.getId());
    }
    private void sendOrderConfirmation(KafkaProducerService.Order order) {
        // 發(fā)送確認(rèn)邏輯
        log.info("發(fā)送訂單確認(rèn): orderId={}", order.getId());
    }
    private void handleInventoryShortage(KafkaProducerService.Order order, Exception e) {
        // 處理庫存不足
        log.warn("處理庫存不足: orderId={}", order.getId());
    }
    private void processSingleRecord(ConsumerRecord<String, Object> record) {
        // 處理單條記錄
        log.info("處理記錄: key={}, value={}", record.key(), record.value());
    }
}

?? 郵件服務(wù)模擬

??郵件服務(wù)實(shí)現(xiàn)??:

@Service
@Slf4j
public class EmailService {
    /**
     * 發(fā)送歡迎郵件
     */
    public void sendWelcomeEmail(String email, String username) {
        log.info("發(fā)送歡迎郵件: 收件人={}, 用戶名={}", email, username);
        // 模擬郵件發(fā)送
        try {
            Thread.sleep(100); // 模擬網(wǎng)絡(luò)延遲
            log.info("歡迎郵件發(fā)送成功: {}", email);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("郵件發(fā)送中斷", e);
        }
    }
    /**
     * 發(fā)送訂單確認(rèn)郵件
     */
    public void sendOrderConfirmation(String email, String orderInfo) {
        log.info("發(fā)送訂單確認(rèn)郵件: 收件人={}, 訂單信息={}", email, orderInfo);
        // 模擬郵件發(fā)送
        try {
            Thread.sleep(150);
            log.info("訂單確認(rèn)郵件發(fā)送成功: {}", email);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("郵件發(fā)送中斷", e);
        }
    }
}

?? Kafka 健康檢查與監(jiān)控

??Kafka 健康指示器??:

@Component
public class KafkaHealthIndicator implements HealthIndicator {
    @Autowired
    private KafkaTemplate<String, Object> kafkaTemplate;
    @Override
    public Health health() {
        try {
            // 測(cè)試Kafka連接
            Future<RecordMetadata> future = kafkaTemplate.send("health-check", "test-key", "test-value");
            // 設(shè)置超時(shí)時(shí)間
            try {
                RecordMetadata metadata = future.get(5, TimeUnit.SECONDS);
                return Health.up()
                    .withDetail("cluster", "kafka")
                    .withDetail("topic", metadata.topic())
                    .withDetail("partition", metadata.partition())
                    .withDetail("status", "connected")
                    .build();
            } catch (TimeoutException e) {
                return Health.down()
                    .withDetail("error", "Kafka connection timeout")
                    .build();
            }
        } catch (Exception e) {
            return Health.down(e)
                .withDetail("error", "Kafka health check failed: " + e.getMessage())
                .build();
        }
    }
}

?? 五、總結(jié)與擴(kuò)展閱讀

?? 整合成果總結(jié)

??三大組件整合對(duì)比??:

組件核心 Starter關(guān)鍵配置主要用途性能優(yōu)化點(diǎn)
Redisspring-boot-starter-data-redis連接池、序列化方式(JDK/JSON)、超時(shí)配置緩存、分布式鎖、會(huì)話存儲(chǔ)? 使用 Lettuce + 連接池復(fù)用
? 采用 JSON 序列化(GenericJackson2JsonRedisSerializer)
? 利用 Pipeline/批量操作 提升吞吐
MyBatismybatis-spring-boot-starter數(shù)據(jù)源、Mapper 掃描、SqlSession 管理數(shù)據(jù)持久化、ORM 映射? 啟用 二級(jí)緩存、合理設(shè)置 緩存刷新策略
? 使用 分頁插件(PageHelper / MyBatis Plus) 減少全表掃描
? 優(yōu)化 SQL 與索引結(jié)構(gòu)
Kafkaspring-kafkaProducer/Consumer 參數(shù)、Topic 配置異步消息處理、事件驅(qū)動(dòng)架構(gòu)? 啟用 批量發(fā)送 (batch.size, linger.ms)
? 消費(fèi)端開啟 多線程并發(fā)消費(fèi)
? 合理設(shè)置 acks、retries、冪等性 提高可靠性

?? 性能優(yōu)化建議

??Redis 優(yōu)化策略??:

# 生產(chǎn)環(huán)境 Redis 優(yōu)化配置
spring:
  redis:
    lettuce:
      pool:
        max-active: 50
        max-idle: 20
        min-idle: 10
    timeout: 1000ms

?MyBatis 優(yōu)化建議??:

// 啟用二級(jí)緩存
@CacheNamespace
public interface UserMapper {
    // Mapper 接口
}
// 使用批量操作
@Insert("<script>INSERT INTO user (...) VALUES " +
       "<foreach collection='list' item='item' separator=','>(...)</foreach></script>")
void batchInsert(List<User> users);

??Kafka 性能調(diào)優(yōu)??:

spring:
  kafka:
    producer:
      batch-size: 32768      # 增大批量大小
      linger-ms: 20          # 適當(dāng)增加延遲
      compression-type: snappy # 啟用壓縮
    consumer:
      fetch-max-wait: 500    # 最大等待時(shí)間
      fetch-min-size: 1024   # 最小獲取大小

到此這篇關(guān)于Spring Boot 整合第三方組件Redis、MyBatis、Kafka 實(shí)戰(zhàn)案例指南的文章就介紹到這了,更多相關(guān)Spring Boot 整合Redis、MyBatis、Kafka內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Spring-IOC容器中的常用注解與使用方法詳解

    Spring-IOC容器中的常用注解與使用方法詳解

    Spring是一個(gè)輕量級(jí)Java開發(fā)框架,最早有Rod Johnson創(chuàng)建,目的是為了解決企業(yè)級(jí)應(yīng)用開發(fā)的業(yè)務(wù)邏輯層和其他各層的耦合問題,這篇文章給大家詳細(xì)介紹Spring-IOC容器中的常用注解與使用方法,感興趣的朋友跟隨小編一起看看吧
    2021-04-04
  • Spring?invokeBeanFactoryPostProcessors方法刨析源碼

    Spring?invokeBeanFactoryPostProcessors方法刨析源碼

    invokeBeanFactoryPostProcessors該方法會(huì)實(shí)例化所有BeanDefinitionRegistryPostProcessor和BeanFactoryPostProcessor的實(shí)例并且執(zhí)行postProcessBeanFactory與postProcessBeanDefinitionRegistry方法
    2023-01-01
  • Java?Cookie與Session實(shí)現(xiàn)會(huì)話跟蹤詳解

    Java?Cookie與Session實(shí)現(xiàn)會(huì)話跟蹤詳解

    session的工作原理和cookie非常類似,在cookie中存放一個(gè)sessionID,真實(shí)的數(shù)據(jù)存放在服務(wù)器端,客戶端每次發(fā)送請(qǐng)求的時(shí)候帶上sessionID,服務(wù)端根據(jù)sessionID進(jìn)行數(shù)據(jù)的響應(yīng)
    2022-11-11
  • 詳解Java發(fā)送HTTP請(qǐng)求

    詳解Java發(fā)送HTTP請(qǐng)求

    這篇文章主要介紹了Java發(fā)送HTTP請(qǐng)求,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • java使用spring實(shí)現(xiàn)發(fā)送mail的方法

    java使用spring實(shí)現(xiàn)發(fā)送mail的方法

    這篇文章主要介紹了java使用spring實(shí)現(xiàn)發(fā)送mail的方法,涉及java基于spring框架發(fā)送郵件的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-10-10
  • Java事件機(jī)制要素及實(shí)例詳解

    Java事件機(jī)制要素及實(shí)例詳解

    這篇文章主要介紹了Java事件機(jī)制要素及實(shí)例詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-04-04
  • IDEA自定義pom依賴的步驟詳解

    IDEA自定義pom依賴的步驟詳解

    這篇文章主要介紹了IDEA自定義pom依賴的步驟詳解,本文分步驟通過圖文并茂的形式給大家介紹的非常詳細(xì)對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-04-04
  • SpringBoot集成Quartz實(shí)現(xiàn)持久化定時(shí)接口調(diào)用任務(wù)

    SpringBoot集成Quartz實(shí)現(xiàn)持久化定時(shí)接口調(diào)用任務(wù)

    Quartz是功能強(qiáng)大的開源作業(yè)調(diào)度庫,幾乎可以集成到任何?Java?應(yīng)用程序中,從最小的獨(dú)立應(yīng)用程序到最大的電子商務(wù)系統(tǒng),本文將通過代碼示例給大家介紹SpringBoot集成Quartz實(shí)現(xiàn)持久化定時(shí)接口調(diào)用任務(wù),需要的朋友可以參考下
    2023-07-07
  • IDEA提示`SQL dialect is not configured`的問題分析及解決方案

    IDEA提示`SQL dialect is not configured`的問題

    在Java開發(fā)中,尤其是使用IntelliJ IDEA或MyBatis等框架時(shí),開發(fā)者常會(huì)遇到 SQL dialect is not configured 的警告或錯(cuò)誤,這一問題不僅影響代碼的高亮和智能提示功能,還可能導(dǎo)致表結(jié)構(gòu)解析失敗、語法校驗(yàn)失效等問題,需要的朋友可以參考下
    2025-06-06
  • 詳解MyBatis的XML實(shí)現(xiàn)方法(附帶注解方式實(shí)現(xiàn))

    詳解MyBatis的XML實(shí)現(xiàn)方法(附帶注解方式實(shí)現(xiàn))

    這篇文章主要詳細(xì)介紹了MyBatis的XML實(shí)現(xiàn)方法(附帶注解方式實(shí)現(xiàn)),文中通過代碼示例給大家講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2024-05-05

最新評(píng)論

凤阳县| 个旧市| 晋宁县| 社旗县| 连城县| 张家港市| 甘孜县| 靖州| 安龙县| 年辖:市辖区| 庆元县| 德令哈市| 双江| 双柏县| 新田县| 庆元县| 长泰县| 黄山市| 大埔县| 卓尼县| 平度市| 武功县| 腾冲县| 菏泽市| 石泉县| 大连市| 天等县| 焦作市| 特克斯县| 保山市| 游戏| 锦屏县| 四川省| 河津市| 红桥区| 沁水县| 伊吾县| 石林| 藁城市| 永宁县| 西安市|