Spring Boot 項目開發實戰:入門應用部分原理示例講解

前言

Spring Boot 作為當前 Java 開發領域最流行的框架之一,以其 "約定優于配置" 的理念極大簡化了企業級應用的開發流程。本文將基于《Spring Boot 項目開發教程(慕課版)》中的資產管理系統項目,深入解析 Spring Boot 的核心技術模塊,并通過實戰代碼示例展示各模塊的實際應用。

一、Spring Boot 核心配置

Spring Boot 的核心優勢在于其自動化配置機制,通過極少的配置即可快速搭建生產級應用。以下是一個標準 Spring Boot 項目的啟動類與基礎配置示例:

// AssetsManagerApplication.java類
@SpringBootApplication
@EnableCaching
@EnableScheduling
public class AssetsManagerApplication {public static void main(String[] args) {SpringApplication.run(AssetsManagerApplication.class, args);}
}# application.yml
server:port: 8097tomcat:uri-encoding: UTF-8max-connections: 10000spring:datasource:url: jdbc:mysql://localhost:3306/am?useUnicode=true&characterEncoding=utf8username: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Driverthymeleaf:cache: falseencoding: UTF-8

上述代碼展示了 Spring Boot 項目的基本結構:啟動類通過?@SpringBootApplication?注解開啟自動配置,配置文件中定義了服務器端口、數據庫連接等基礎參數。Spring Boot 會自動掃描主類所在包及其子包中的組件,并根據類路徑中的依賴自動配置相關組件。

1.自定義配置與多環境管理

在實際開發中,常需要自定義配置或針對不同環境(開發、測試、生產)進行差異化配置:

// MyConfiguration.java類
@Configuration
public class MyConfiguration {@Beanpublic PaginationInterceptor paginationInterceptor() {PaginationInterceptor paginationInterceptor = new PaginationInterceptor();paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));return paginationInterceptor;}
}# application-dev.yml
spring:profiles: devdatasource:url: jdbc:mysql://dev-server:3306/am_dev# application-prod.yml
spring:profiles: proddatasource:url: jdbc:mysql://prod-server:3306/am_prod

通過?@Configuration?注解定義配置類,使用?@Bean?注解注冊自定義組件。多環境配置通過?application-{profile}.yml?文件實現,通過?spring.profiles.active?屬性指定當前激活的環境。

詳細講解如下:
1. 自定義配置的核心機制

在Spring Boot中,@Configuration注解標記一個類為配置類,該類包含bean定義方法。@Bean注解用于方法上,表示該方法創建的對象將由Spring容器管理為一個bean。這允許自定義組件,如攔截器、服務或數據源。

  • 為什么使用:這避免了XML配置,使代碼更簡潔。例如,在您的MyConfiguration類中,paginationInterceptor()方法定義了一個分頁攔截器bean,Spring在啟動時會自動實例化并注入它。
  • 關鍵點:bean的生命周期由Spring管理,支持依賴注入(如通過構造函數或setter注入其他bean)。
2. 多環境配置的實現方式

多環境配置通過application-{profile}.yml文件實現,其中{profile}是環境標識(如devtestprod)。這可以通過spring.profiles.active屬性指定當前激活的環境。

  • 激活環境:在默認的application.yml文件中設置spring.profiles.active=dev,Spring Boot會自動加載application-dev.yml中的配置。例如:
# application.yml (主配置文件)
spring:profiles:active: dev  # 指定激活開發環境
  • 優勢:環境隔離配置(如數據庫URL、API密鑰),避免硬編碼,提升安全性和可維護性。在您的例子中,application-dev.ymlapplication-prod.yml分別定義了開發和生產環境的數據庫連接。
3. 具體示例擴展

下面是常見場景的示例

示例1: 自定義數據源bean(擴展@Bean用法)

假設您需要根據不同環境使用不同的數據源(如開發環境用H2內存數據庫,生產環境用MySQL)。可以在配置類中定義bean,并利用環境變量注入屬性。

// CustomDataSourceConfig.java類
@Configuration
public class CustomDataSourceConfig {@Value("${spring.datasource.url}")  // 從YAML注入URLprivate String url;@Beanpublic DataSource dataSource() {// 根據環境變量創建數據源HikariDataSource dataSource = new HikariDataSource();dataSource.setJdbcUrl(url);return dataSource;}
}

在YAML文件中定義環境特定屬性:

# application-dev.yml
spring:profiles: devdatasource:url: jdbc:h2:mem:testdb  # 開發環境用內存數據庫# application-prod.yml
spring:profiles: proddatasource:url: jdbc:mysql://prod-server:3306/am_prod  # 生產環境用真實數據庫

  • 效果:當spring.profiles.active=dev時,dataSource()方法使用H2 URL;在prod環境下,自動切換到MySQL。
示例2: 多環境日志配置(擴展YAML文件)

不同環境可能需要不同的日志級別(如開發環境輸出詳細日志,生產環境僅記錄錯誤)。通過YAML文件實現:

# application-dev.yml
spring:profiles: dev
logging:level:root: DEBUG  # 開發環境:詳細日志# application-prod.yml
spring:profiles: prod
logging:level:root: ERROR  # 生產環境:僅錯誤日志
  • 無需額外代碼:Spring Boot自動應用這些配置,無需修改日志框架代碼。
示例3: 自定義服務bean(請自主結合自身環境屬性定義配置)

假設您有一個郵件服務,在開發環境使用模擬發送,在生產環境使用真實SMTP。定義bean時注入環境相關屬性:

// MailServiceConfig.java類
@Configuration
public class MailServiceConfig {@Value("${mail.enabled}")  // 從YAML注入是否啟用private boolean enabled;@Value("${mail.host}")  // 注入主機地址private String host;@Beanpublic MailService mailService() {return new MailService(enabled, host);  // 創建bean,參數來自環境配置}
}

在YAML文件中設置環境特定值:

# application-dev.yml
spring:profiles: dev
mail:enabled: false  # 開發環境禁用真實發送host: localhost# application-prod.yml
spring:profiles: prod
mail:enabled: true   # 生產環境啟用host: smtp.prod-server.com
  • 效果:bean行為隨環境變化,提高代碼靈活性,

注:通常解決環境差異導致的配置問題, 特別適合微服務架構或持續集成/持續部署(CI/CD)流程中處理環境相關配置。

二、數據庫操作與持久層框架整合

Spring Boot 對主流持久層框架提供了良好的整合支持,以下是三種常見持久層方案的整合示例:

1. JdbcTemplate 基礎操作

// SysRoleDaoImpl.java類
@Repository
public class SysRoleDaoImpl implements SysRoleDao {@Autowiredprivate JdbcTemplate jdbcTemplate;@Overridepublic int saveSys_role(Sys_role sys_role) {String sql = "INSERT INTO sys_role(name, description) VALUES(?, ?)";return jdbcTemplate.update(sql, sys_role.getName(), sys_role.getDescription());}@Overridepublic List<Sys_role> getAllSys_role() {String sql = "SELECT * FROM sys_role";return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Sys_role.class));}
}

JdbcTemplate 提供了簡單的 SQL 操作封裝,適合快速開發簡單數據訪問場景,但在復雜業務場景下 SQL 語句維護較為繁瑣。

解釋回答:JdbcTemplate 作為 Spring 提供的 JDBC 模板工具,確實在簡單數據訪問場景下表現出色,但在復雜業務場景中,由于 SQL 語句需要硬編碼且缺乏 ORM 映射能力,會暴露出以下常見問題:

1.1 動態 SQL 構建復雜

當業務需要根據不同條件動態拼接 SQL 時,JdbcTemplate 需要手動處理條件拼接,容易出現 SQL 注入風險或語法錯誤。

// 示例:復雜條件查詢用戶列表(動態條件拼接)
public List<User> findUsersByConditions(String name, Integer age, String role, Boolean active) {StringBuilder sql = new StringBuilder("SELECT * FROM users WHERE 1=1");List<Object> params = new ArrayList<>();if (name != null && !name.isEmpty()) {sql.append(" AND name LIKE ?");params.add("%" + name + "%");}if (age != null) {sql.append(" AND age > ?");params.add(age);}if (role != null && !role.isEmpty()) {sql.append(" AND role = ?");params.add(role);}if (active != null) {sql.append(" AND active = ?");params.add(active);}// 可能還需要添加排序、分頁等條件sql.append(" ORDER BY create_time DESC");try {return jdbcTemplate.query(sql.toString(), params.toArray(), (rs, rowNum) -> new User(rs.getInt("id"),rs.getString("name"),rs.getInt("age"),rs.getString("role"),rs.getBoolean("active")));} catch (DataAccessException e) {log.error("動態SQL查詢失敗: {}", sql.toString(), e);throw new BusinessException("查詢失敗");}
}

問題分析

  • SQL 語句通過字符串拼接實現,可讀性差且容易出錯
  • 條件判斷邏輯與 SQL 語句耦合,維護困難
  • 結果集映射需要手動處理每一個字段,重復代碼多
  • 缺乏類型安全檢查,參數類型錯誤可能在運行時才發現

1.2. 復雜關聯查詢難以維護

涉及多表關聯、子查詢的復雜查詢,SQL 語句長度和復雜度急劇增加,JdbcTemplate 難以管理。

// 示例:多表關聯查詢資產領用記錄(資產表、部門表、用戶表關聯)
public List<AssetRecordVO> getAssetRecordsWithDetails() {String sql = "SELECT " +"a.id as asset_id, a.name as asset_name, a.specification, " +"d.id as dept_id, d.name as dept_name, " +"u.id as user_id, u.username, " +"r.quantity, r.apply_time, r.status " +"FROM asset a " +"JOIN asset_record r ON a.id = r.asset_id " +"JOIN department d ON r.dept_id = d.id " +"JOIN users u ON r.user_id = u.id " +"WHERE r.status = 'APPROVED' " +"ORDER BY r.apply_time DESC";try {return jdbcTemplate.query(sql, (rs, rowNum) -> {AssetRecordVO vo = new AssetRecordVO();vo.setAssetId(rs.getInt("asset_id"));vo.setAssetName(rs.getString("asset_name"));vo.setSpecification(rs.getString("specification"));vo.setDeptId(rs.getInt("dept_id"));vo.setDeptName(rs.getString("dept_name"));vo.setUserId(rs.getInt("user_id"));vo.setUsername(rs.getString("username"));vo.setQuantity(rs.getInt("quantity"));vo.setApplyTime(rs.getTimestamp("apply_time"));vo.setStatus(rs.getString("status"));return vo;});} catch (DataAccessException e) {log.error("關聯查詢失敗: {}", sql, e);throw new BusinessException("查詢資產記錄失敗");}
}

問題分析

  • SQL 語句長達數十行,難以閱讀和調試
  • 字段別名映射容易出錯(如a.id as asset_id
  • 結果集映射需要手動處理所有關聯表的字段
  • 當表結構變更時,SQL 和映射代碼都需要修改
  • 缺乏 SQL 重構支持,修改字段順序或添加條件成本高

1.3. 事務管理復雜性

復雜業務可能涉及多個數據庫操作的事務管理,JdbcTemplate 需要手動處理事務邊界。

// 示例:資產領用與庫存扣減的事務操作
@Transactional
public void processAssetIssue(Long assetId, Integer quantity, Long userId) {// 1. 查詢資產庫存String checkSql = "SELECT quantity FROM asset_stock WHERE asset_id = ?";Integer currentStock = jdbcTemplate.queryForObject(checkSql, Integer.class, assetId);if (currentStock < quantity) {throw new BusinessException("庫存不足,無法領用");}// 2. 扣減庫存String updateStockSql = "UPDATE asset_stock SET quantity = quantity - ? WHERE asset_id = ?";int updateStockResult = jdbcTemplate.update(updateStockSql, quantity, assetId);if (updateStockResult != 1) {throw new BusinessException("庫存更新失敗");}// 3. 創建領用記錄String insertRecordSql = "INSERT INTO asset_issue (asset_id, quantity, user_id, issue_time) " +"VALUES (?, ?, ?, NOW())";int insertRecordResult = jdbcTemplate.update(insertRecordSql, assetId, quantity, userId);if (insertRecordResult != 1) {// 手動回滾需要額外代碼,@Transactional在此處已失效throw new BusinessException("領用記錄創建失敗");}// 4. 發送通知(假設需要調用外部服務,可能拋出異常)notifyUserOfIssue(userId, assetId, quantity);
}

問題分析

  • 事務邊界通過@Transactional注解實現,但復雜邏輯中難以覆蓋所有異常場景
  • 手動檢查更新結果(如updateStockResult != 1)增加代碼復雜度
  • 跨服務調用(如notifyUserOfIssue)拋出異常時,事務回滾依賴 Spring 的默認規則
  • 缺乏聲明式事務的高級控制(如事務傳播行為、超時設置)
  • 多個 SQL 操作的一致性保證依賴開發者手動處理

1.4. 分頁與排序處理繁瑣

復雜業務中的分頁查詢需要手動處理 SQL 分頁語法,不同數據庫方言需要不同處理。

// 示例:帶復雜條件的分頁查詢(MySQL方言)
public Page<Asset> getAssetsWithPagination(AssetQueryCriteria criteria, int page, int size) {StringBuilder sql = new StringBuilder("SELECT * FROM assets WHERE 1=1");List<Object> params = new ArrayList<>();// 構建查詢條件if (criteria.getName() != null) {sql.append(" AND name LIKE ?");params.add("%" + criteria.getName() + "%");}if (criteria.getTypeId() != null) {sql.append(" AND type_id = ?");params.add(criteria.getTypeId());}if (criteria.getStatus() != null) {sql.append(" AND status = ?");params.add(criteria.getStatus());}// 計算總數String countSql = "SELECT COUNT(*) " + sql.toString();int total = jdbcTemplate.queryForObject(countSql, Integer.class, params.toArray());// 構建分頁SQL(MySQL)String pageSql = sql.toString() + " ORDER BY create_time DESC LIMIT ? OFFSET ?";params.add(size);params.add((page - 1) * size);List<Asset> items = jdbcTemplate.query(pageSql, params.toArray(), (rs, rowNum) -> {Asset asset = new Asset();asset.setId(rs.getLong("id"));asset.setName(rs.getString("name"));asset.setTypeId(rs.getLong("type_id"));asset.setStatus(rs.getString("status"));asset.setPurchaseDate(rs.getDate("purchase_date"));return asset;});return Page.of(items, PageRequest.of(page - 1, size), total);
}

問題分析

  • 分頁邏輯需要手動拼接LIMITOFFSET參數
  • 不同數據庫(如 Oracle 的ROWNUM、SQL Server 的OFFSET-FETCH)需要不同分頁語法
  • 總數查詢和分頁查詢分離,增加代碼量
  • 排序條件硬編碼在 SQL 中,難以動態調整
  • 缺乏分頁參數的類型安全檢查

1.5. 批量操作性能問題

大量數據的批量操作(如批量插入、更新)需要手動拼接 SQL,性能優化困難。

// 示例:批量更新資產狀態(1000條記錄)
public void batchUpdateAssetStatus(List<Long> assetIds, String newStatus) {if (assetIds == null || assetIds.isEmpty()) {return;}// 手動拼接IN條件(注意:超過數據庫IN參數限制會報錯)StringBuilder sql = new StringBuilder("UPDATE assets SET status = ? WHERE id IN (");for (int i = 0; i < assetIds.size(); i++) {sql.append("?");if (i < assetIds.size() - 1) {sql.append(", ");}}sql.append(")");// 構建參數列表(第一個參數是status,后面是ids)List<Object> params = new ArrayList<>();params.add(newStatus);params.addAll(assetIds);try {jdbcTemplate.update(sql.toString(), params.toArray());} catch (DataAccessException e) {log.error("批量更新失敗: {}", sql.toString(), e);throw new BusinessException("批量更新資產狀態失敗");}
}

問題分析

  • 批量操作通過 IN 條件實現,受數據庫參數數量限制(如 MySQL 默認限制為 1000 個參數)
  • 大數量批量操作可能導致 SQL 語句過長,超出數據庫協議限制
  • 缺乏批量操作的優化支持(如 JDBC 的 batch update)
  • 錯誤處理顆粒度粗,無法知道哪條記錄更新失敗
  • 性能受 SQL 解析和參數綁定影響,不如 ORM 框架的批量操作優化

1.6.總結:JdbcTemplate 的適用場景與替代方案
  • 適用場景
    • 簡單 CRUD 操作(如單表查詢、插入、更新)
    • 原型開發或快速驗證業務邏輯
    • 對性能要求極高且 SQL 邏輯簡單的場景
  • 復雜場景替代方案
    • MyBatis/MyBatis-Plus:通過 XML 或注解管理 SQL,支持動態 SQL 和結果映射
    • Spring Data JPA:基于 JPA 規范的聲明式查詢,適合領域模型驅動開發
    • ORM 框架(如 Hibernate):完全對象 - 關系映射,減少 SQL 編碼

注:在企業級復雜業務中,建議根據業務復雜度選擇合適的持久層方案,JdbcTemplate 更適合作為簡單場景的輔助工具,而非核心解決方案。

2. MyBatis-Plus 高級封裝

// SysDepartmentMapper.java類
@Mapper
public interface SysDepartmentMapper extends BaseMapper<SysDepartment> {List<SysDepartmentListResp> list(@Param("params") SysDepartmentListReq params,@Param("offset") Integer offset, @Param("limit") Integer limit);
}// SysDepartmentServiceImpl.java
@Service
public class SysDepartmentServiceImpl implements SysDepartmentService {@Autowiredprivate SysDepartmentMapper sysDepartmentMapper;@Overridepublic SysDepartmentVo queryList(Integer current, Integer size) {SysDepartmentVo vo = new SysDepartmentVo();IPage<SysDepartment> page = new Page<>(current, size);sysDepartmentMapper.selectPage(page, null);vo.setSys_departmentList(page.getRecords());vo.setTotal(page.getTotal());return vo;}
}

MyBatis-Plus 在 MyBatis 基礎上提供了強大的 CRUD 封裝和分頁插件,通過繼承?BaseMapper?即可獲得基礎操作能力,復雜查詢可通過自定義 SQL 實現。

注: MyBatis-Plus 并未改變 MyBatis 的核心機制,而是通過繼承擴展插件機制為其添加了更多實用功能(如:?基礎 CRUD 操作自動化、物理分頁插件、動態條件構造器、代碼生成器、性能分析插件、邏輯刪除支持),避免了重復編寫基礎 CRUD 代碼。其設計遵循 “約定優于配置” 原則,通過命名規范和默認實現減少開發量。

附:MyBatis-Plus 與 MyBatis 的對比

3. Spring Data JPA 聲明式查詢

// SysAssetTypeDao.java
public interface SysAssetTypeDao extends JpaRepository<SysAssetType, Integer> {List<SysAssetType> getSysAssetTypeByIdEquals(Integer id);List<SysAssetType> getSysAssetTypeByNameStartingWith(String name);@Query("select s from sys_asset_type s")List<SysAssetType> getAllSysAssetType();
}// SysAssetTypeServiceImpl.java
@Service
public class SysAssetTypeServiceImpl implements SysAssetTypeService {@Autowiredprivate SysAssetTypeDao sysAssetTypeDao;@Overridepublic void saveSysAssetType(SysAssetType sysAssetType) {sysAssetTypeDao.save(sysAssetType);}
}

Spring Data JPA 基于 JPA 規范提供了聲明式查詢能力,通過方法名約定或?@Query?注解即可實現數據訪問,適合領域模型清晰的場景

場景解釋:

Spring Data JPA 的適用場景邊界

  • 適合場景

    1. 業務領域概念明確(如資產管理系統中的資產類型、領用記錄等);
    2. 實體類設計遵循業務驅動(而非數據庫驅動);
    3. 查詢以單表操作、簡單條件查詢為主。
  • 不適合場景

    1. 領域模型頻繁變更或尚未明確;
    2. 存在大量復雜 SQL(如多表嵌套子查詢、動態分組統計);
    3. 對 SQL 性能要求極致優化(需手動編寫原生 SQL)。

注:Spring Data JPA 的核心優勢在于 “以領域模型為橋梁,將對象操作映射為數據訪問”,這要求領域模型必須與業務概念高度一致,否則其聲明式查詢的便捷性將大打折扣。

三、Web 項目開發與視圖層整合

Spring Boot 對 Web 開發的支持以 Spring MVC 為基礎,結合 Thymeleaf 模板引擎可快速構建前后端不分離應用:

前端:

<!-- sysPurchaseRecord.html -->
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>資產采購列表</title>
</head>
<body><table class="table"><thead><tr><th>采購人</th><th>資產名稱</th><th>采購狀態</th></tr></thead><tbody><tr th:each="spr, iterStat : ${sysPurchaseRecords}"><td th:text="${spr.buyerName}"></td><td th:text="${spr.assetName}"></td><td th:text="${spr.purchaseStatus == 1 ? '采購中' : '已完成'}"></td></tr></tbody></table>
</body>
</html>

后端:

// SysPurchaseRecordController.java
@RestController
@RequestMapping("/sysPurchaseRecord")
public class SysPurchaseRecordController {@Resourceprivate SysPurchaseRecordService sysPurchaseRecordService;@ApiOperation(value = "采購列表")@GetMapping("/list")public ChorResponse<Map<String, Object>> list(@ModelAttribute SysPurchaseRecordListReq req) {return ChorResponseUtils.success(sysPurchaseRecordService.list(req));}
}

Thymeleaf 模板引擎支持在 HTML 中直接嵌入數據展示邏輯,通過?th:?前綴的屬性實現數據綁定和條件渲染。控制層通過?@RestController?注解返回 JSON 數據,或通過?@Controller?配合視圖解析器返回頁面。

附:

四、緩存與消息隊列集成

1. Redis 緩存實現

// RedisCacheConfig.java類
@Configuration
public class RedisCacheConfig extends CachingConfigurerSupport {@Beanpublic RedisTemplate<String, Object> redisTemplate() {RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer();redisTemplate.setValueSerializer(serializer);redisTemplate.setKeySerializer(new StringRedisSerializer());redisTemplate.setConnectionFactory(connectionFactory);return redisTemplate;}@Beanpublic CacheManager cacheManager() {RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(600)).serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())).disableCachingNullValues();return RedisCacheManager.builder(connectionFactory).cacheDefaults(config).build();}
}// SysAssetServiceImpl.java類
@Service
public class SysAssetServiceImpl implements SysAssetService {@Cacheable(cacheNames = {"sysAsset"}, key = "#id")@Overridepublic SysAssetResp find(Long id) {SysAssetResp sysAssetResp = sysAssetMapper.getOne(id);// 業務邏輯處理return sysAssetResp;}
}

通過 Spring Cache 注解體系結合 Redis 實現緩存功能,@Cacheable?注解自動將方法返回值存入緩存,@CacheEvict?用于緩存失效,@CachePut?用于緩存更新。

Redis 實現 Spring Boot 緩存功能的原理示意圖

1.1 擴展:Redis 緩存工作原理詳解

1.1.1 @Cacheable 執行流程
// 偽代碼實現邏輯
public Object cachedMethod(參數) {1. 生成緩存Key (如 "sysAsset::123")2. 調用 cacheManager.getCache("sysAsset").get(key)3. if (緩存存在) {return 反序列化(緩存值); // 直接返回}4. 執行實際方法體 (數據庫查詢等)5. 將結果存入緩存: cache.put(key, 序列化(結果))6. 返回結果
}
1.1.2 關鍵配置解析
@Bean
public CacheManager cacheManager() {RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(600)) // 10分鐘過期.serializeKeysWith(StringRedisSerializer.INSTANCE) // Key序列化.serializeValuesWith(Jackson2JsonRedisSerializer.INSTANCE) // Value序列化.disableCachingNullValues(); // 不緩存nullreturn RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(config).build();
}
1.1.3.緩存注解對比

1.1.4?監控與調試:
使用 redis-cli monitor 命令觀察緩存操作
啟用 Spring Boot Actuator 的 cache 端點
日志配置:logging.level.org.springframework.cache=DEBUG

2. RabbitMQ 消息隊列

// TopicRabbitMQConfig.java類
@Configuration
public class TopicRabbitMQConfig {@Value("${rabbitmq.queue}")private String queue;@Value("${rabbitmq.exchange}")private String exchange;@Value("${rabbitmq.routingKey}")private String routingKey;@Beanpublic TopicExchange getExchangeName() {return new TopicExchange(exchange);}@Beanpublic Queue getQueueName() {return new Queue(queue);}@Beanpublic Binding declareBinding() {return BindingBuilder.bind(getQueueName()).to(getExchangeName()).with(routingKey);}
}// TopicReceiver.java類
@Component
@RabbitListener(queues = "test02")
public class TopicReceiver {private static final Logger log = LoggerFactory.getLogger(TopicReceiver.class);@RabbitHandlerpublic void handleMessage(String message) {log.info("test02 隊列接收到的消息是:{}", message);}
}

RabbitMQ 集成通過配置交換器、隊列和綁定關系實現消息路由,@RabbitListener?注解標注的方法會自動監聽指定隊列的消息并處理。

?RabbitMQ 在 Spring Boot 中集成的原理示意圖

2.1 RabbitMQ 工作原理詳解

1. 核心組件關系

2.TopicExchange 路由規則

  • *?(星號) 匹配一個單詞

  • #?(井號) 匹配零個或多個單詞

routingKey        匹配模式
---------         ---------
asset.create      asset.*
asset.update      asset.*
user.notify       user.#
system.alert      *.*

3.? 消息處理流程

// 生產者發送消息
@Autowired
private RabbitTemplate rabbitTemplate;public void sendMessage(String message) {// 發送到 exchange,指定 routingKeyrabbitTemplate.convertAndSend(exchange, routingKey, message);
}// 消費者處理消息
@Component
@RabbitListener(queues = "test02")
public class TopicReceiver {@RabbitHandlerpublic void handleMessage(String message) {// 處理消息邏輯log.info("接收消息: {}", message);}
}

4. 關鍵配置解析

@Configuration
public class TopicRabbitMQConfig {// 創建 Topic Exchange@Beanpublic TopicExchange topicExchange() {return new TopicExchange("asset-exchange");}// 創建隊列@Beanpublic Queue assetQueue() {return new Queue("asset-queue");}// 綁定隊列到 Exchange@Beanpublic Binding binding(Queue assetQueue, TopicExchange exchange) {// asset.* 匹配 asset.create, asset.update 等return BindingBuilder.bind(assetQueue).to(exchange).with("asset.*");}
}

5. 消息生命周期管理

6. 監控與維護:
使用 RabbitMQ Management UI(默認端口15672)
集成 Spring Boot Actuator 監控端點
關鍵指標監控:消息積壓率、ACK延遲、重試次數

五、安全機制實現

1. JWT 認證

// JwtUtil.java類
public class JwtUtil {public static String createJwt(long ttlMillis, SysUser sysUser, String fillArgs) {SignatureAlgorithm algorithm = SignatureAlgorithm.HS256;long nowMillis = System.currentTimeMillis();Date now = new Date(nowMillis);String key = "token16546461";Map<String, Object> claims = new HashMap<>();claims.put("id", sysUser.getId());claims.put("name", sysUser.getUsername());return Jwts.builder().setClaims(claims).setId(UUID.randomUUID().toString()).setIssuedAt(now).setExpiration(new Date(nowMillis + ttlMillis)).signWith(algorithm, key).compact();}public static Claims parseJwt(String token) throws ChorBizException {String key = "token16546461";return Jwts.parser().setAllowedClockSkewSeconds(604800).setSigningKey(key).parseClaimsJws(token).getBody();}
}// ApiInterceptor.java類
@Component
public class ApiInterceptor extends HandlerInterceptorAdapter {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String token = request.getHeader("Authorization");Claims claims = JwtUtil.parseJwt(token);// 驗證用戶存在性return true;}
}
JWT 認證流程

請求處理流程

JWT 認證通過生成包含用戶信息的令牌實現無狀態認證,請求時通過請求頭攜帶令牌,攔截器驗證令牌有效性。

2. Shiro 授權

// MyShiroRealm.java類
public class MyShiroRealm extends AuthorizingRealm {@Resourceprivate SysUserMapper sysUserMapper;@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {SysUser sysUser = (SysUser) principalCollection.getPrimaryPrincipal();SimpleAuthorizationInfo authorization = new SimpleAuthorizationInfo();List<SysRole> roleInfoList = sysRoleMapper.getRoleList(sysUser.getId());if (roleInfoList != null && !roleInfoList.isEmpty()) {roleInfoList.forEach(role -> authorization.addRole(role.getCode()));List<SysPermission> permissionInfoList = sysPermissionMapper.getPermissionList(role.getId());if (permissionInfoList != null && !permissionInfoList.isEmpty()) {List<String> permissions = permissionInfoList.stream().map(SysPermission::getPermission).collect(Collectors.toList());authorization.addStringPermissions(permissions);}}return authorization;}
}// SysUnitController.java類
@RestController
@RequestMapping("/sysUnit")
public class SysUnitController {@RequiresRoles(value = {"admin"})@PostMapping("/create")public ChorResponse<Void> create(@RequestBody SysUnitReq req) {sysUnitServiceImpl.save(req);return ChorResponseUtils.success();}@RequiresPermissions(value = {"unit:delete"})@DeleteMapping("/{id}")public ChorResponse<Void> remove(@PathVariable Long id) {sysUnitServiceImpl.remove(id);return ChorResponseUtils.success();}
}



JWT 認證與 Shiro 授權的整合原理圖

Shiro 通過 Realm 實現授權信息加載,@RequiresRoles?和?@RequiresPermissions?注解實現方法級別的權限控制。

六、任務管理與異步處理

1. 定時任務

// ScheduleTimer.java類
@Component
public class ScheduleTimer {private Logger logger = LoggerFactory.getLogger(this.getClass());@Resourceprivate SysReceiveRecordMapper sysReceiveRecordMapper;// 每6小時執行一次@Scheduled(cron = "0 0 0/6 * * ?")public void executeUpdateCuTask() {Thread current = Thread.currentThread();logger.info("定時任務線程:{}", current.getName());List<SysReceiveRecord> records = sysReceiveRecordMapper.selectList(new QueryWrapper<SysReceiveRecord>().eq("status", ParamsConstant.RECEIVE_STATUS_RECEIVE));long currentTime = System.currentTimeMillis();for (SysReceiveRecord record : records) {long useTime = currentTime - record.getUpdateTime();int hours = (int) (useTime / 1000 / 3600);logger.info("資產領用時間已達:{}小時", hours);// 發送提醒通知}}
}

通過?@Scheduled?注解實現定時任務,cron 表達式支持復雜時間規則定義,適用于周期性數據處理場景。

2. 異步任務與郵件服務

// AsyncService.java類
@Service
public class AsyncService {@Autowiredprivate JavaMailSender mailSender;@Asyncpublic void sendEmail(String to, String subject, String content) {SimpleMailMessage message = new SimpleMailMessage();message.setTo(to);message.setSubject(subject);message.setText(content);mailSender.send(message);}
}// AssetsReturnService.java類
@Service
public class AssetsReturnService {@Autowiredprivate AsyncService asyncService;public void remindOverdueReturn(Long userId, String assetName) {SysUser user = sysUserMapper.selectById(userId);asyncService.sendEmail(user.getEmail(), "資產歸還提醒", "尊敬的" + user.getUsername() + ",您領用的" + assetName + "已超過歸還期限,請及時處理。");}
}

@Async?注解實現異步方法調用,郵件服務通過?JavaMailSender?接口實現,適用于耗時操作如發送郵件、生成報表等場景。

異步任務與郵件服務的原理示意圖

異步郵件發送完整流程

七、項目部署與最佳實踐

1. 項目打包與部署

<!-- pom.xml 打包配置 -->
<build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><mainClass>com.cg.test.am.AssetsManagerApplication</mainClass><settings><url>http://maven.aliyun.com/nexus/content/groups/public/</url></settings></configuration></plugin></plugins>
</build>

通過 Maven 插件打包可執行 JAR 包,部署命令:java -jar assets-manager-1.0.0.jar --spring.profiles.active=prod

2. 企業級應用最佳實踐

  • 分層架構:嚴格遵循 Controller-Service-Dao 分層,各層職責清晰
  • 接口規范:采用 RESTful 接口設計,統一響應格式
  • 異常處理:全局異常處理器統一處理業務異常
  • 日志規范:使用統一日志格式,區分業務日志和系統日志
  • 監控告警:集成 Actuator 監控端點,配置健康檢查和告警機制

結語

Spring Boot 通過自動化配置和開箱即用的組件集成,極大降低了企業級應用的開發門檻。本文通過資產管理系統項目實例,全面展示了 Spring Boot 從基礎配置到高級特性的完整應用鏈條。在實際開發中,開發者應根據項目規模和業務復雜度,靈活選擇合適的技術方案,同時遵循最佳實踐,構建可維護、可擴展的高質量應用系統。

隨著微服務架構的普及,Spring Boot 與 Spring Cloud 的結合將成為企業級應用開發的主流方向,后續可進一步探索服務注冊與發現、配置中心、鏈路追蹤等高級主題,實現更復雜的分布式系統架構。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/88200.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/88200.shtml
英文地址,請注明出處:http://en.pswp.cn/web/88200.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ByteBrain x 清華 VLDB25|時序多模態大語言模型 ChatTS

資料來源&#xff1a;火山引擎-開發者社區 近年來&#xff0c;多模態大語言模型&#xff08;MLLM&#xff09;發展迅速&#xff0c;并在圖像、視頻、音頻等領域取得了突破性成果。然而&#xff0c;相較于這些研究較為成熟的模態&#xff0c;時間序列這一類型的數據與大模型結合…

WPF學習筆記(25)MVVM框架與項目實例

MVVM框架與項目實例一、MVVM框架1. 概述2. 核心組件與優勢一、MVVM項目1.普通項目2. MVVM架構3. MVVM項目實例1. 項目準備2. LoginViewModel與Login2. MainWindowViewModel4. MVVM項目優化1. BaseViewModel2. RealyCommand3. 效果展示總結一、MVVM框架 1. 概述 官方文檔&…

MySQL實操

## 基于MySQL#先啟動MySQL服務#第一次登錄[rootlocalhost ~]# mysql -uroot -P3306#密碼登錄[rootlocalhost ~]# mysql -uroot -pEnter password: Welcome to the MySQL monitor. Commands end with ; or \g.Your MySQL connection id is 9Server version: 8.0.41 Source dist…

ez_rust_writeup

一道簡單的[[rust逆向]] #rust逆向 #位運算 題目信息 文件名&#xff1a;ezrust.exe 題目附件&#xff1a;https://wwfj.lanzoul.com/iczMR30k5j4h 密碼:bueq 題目分析 1. 初步分析 這是一道Rust編寫的逆向題目。通過IDA分析可以看到&#xff0c;這是一個典型的flag驗證程序。 …

【QT】-隱式轉換 explicit用法

通俗易懂的解釋:隱式轉換 vs 顯式轉換 什么是隱式轉換? 隱式轉換就是編譯器偷偷幫你做的類型轉換,你甚至都沒意識到它發生了。 例子: cpp 運行 double x = 5; // 隱式:int → double(5 變成 5.0) int y = x * 2.5; // 隱式:double → int(截斷小數部分) 構造函數的隱…

Django核心知識點詳解:JSON、AJAX、Cookie、Session與用戶認證

1. JSON數據格式詳解1.1 什么是JSON&#xff1f;JSON&#xff08;JavaScript Object Notation&#xff09;是一種輕量級的數據交換格式&#xff0c;具有以下特點&#xff1a;獨立于語言&#xff0c;幾乎所有編程語言都支持易于人閱讀和編寫易于機器解析和生成基于文本&#xff…

[特殊字符] Python 實戰 | 批量統計中文文檔詞頻并導出 Excel

本文展示如何用 Python 腳本&#xff1a; 批量讀取文件夾中的多篇中文文檔&#xff1b; 用 jieba 分詞并統計詞頻&#xff08;過濾停用詞與單字符&#xff09;&#xff1b; 將各文檔詞頻輸出為對應 Excel 文件&#xff1b; 是文本分析、內容審查、報告編寫中的實用技巧。 &…

共享打印機(詳細操作+常見問題:需輸入用戶名密碼、無法連接等)

文章目錄一、設置打印機共享的準備工作二、Windows系統下打印機共享設置1. 啟用主機打印機共享2. 客戶端添加共享打印機三、我所遇到的問題及解決方法客戶機遇到輸入用戶名、密碼錯誤代碼 0x0000011b一、錯誤代碼 0x0000011b 的含義二、解決方法添加打印機沒成功其他問題此次打…

在 Windows 系統上配置 [go-zero](https://go-zero.dev) 開發環境教程

&#x1f4bb; 在 Windows 系統上配置 go-zero 開發環境教程 本教程將詳細介紹如何在 Windows 系統上配置 go-zero 微服務框架的開發環境&#xff0c;包括依賴安裝、路徑配置、常見問題等。 &#x1f9f1; 一、前置環境安裝 1. 安裝 Go 下載地址&#xff1a;https://go.dev/…

開源=白嫖?

國內有一個非常濃重的思想&#xff0c;開源&#xff0c;開源就是免費&#xff0c;就是白嫖&#xff0c;就是不花錢&#xff0c;白給。那么什么是開源&#xff1f;“源代碼”是軟件中大多數計算機用戶從未見過的部分;它是計算機程序員可以操縱的代碼&#xff0c;以改變一個軟件(…

2048-控制臺版本

2048控制臺版 文章目錄2048控制臺版實現效果&#xff1a;在這里插入圖片描述庫函數使用&#xff1a;初始化變量功能函數實現&#xff1a;狀態判斷函數int Judge&#xff08;&#xff09;&#xff1b;數字生成函數 bool CtreateNumber&#xff08;&#xff09;打印游戲界面 void…

提取出Wallpaper Engine壁紙的mpkg類靜態壁紙

github 地址 https://github.com/notscuffed/repkg先下載軟件2853…26目錄這樣獲取有的直接mp4格式&#xff0c;就不能用這方法準備好后 cmd 進入repkg目錄 執行 repkg extract ./294...333/scene.pkg

AI健康小屋“15分鐘服務圈”:如何重構社區健康生態?

AI健康小屋作為“15分鐘服務圈”的核心載體&#xff0c;通過技術賦能與場景重構&#xff0c;正推動社區健康生態從被動治療向主動預防、從單一服務向全周期管理轉型。那我們應該如何重構社區健康生態呢&#xff1f;服務模式創新1.全時段覆蓋AI健康小屋通過分時段服務滿足不同群…

[netty5: WebSocketFrame]-源碼分析

WebSocketFrame WebSocketFrame 是 Netty 中用于表示 WebSocket 消息幀的抽象基類&#xff0c;封裝了幀的內容、分片標志和擴展位信息&#xff0c;供各類具體幀&#xff08;如文本、二進制、控制幀&#xff09;繼承使用。 public abstract class WebSocketFrame extends Buffer…

【加解密與C】非對稱加解密(三)ECC橢圓曲線

ECC橢圓曲線的基本概念橢圓曲線密碼學&#xff08;Elliptic Curve Cryptography&#xff0c;ECC&#xff09;是一種基于橢圓曲線數學的公鑰密碼體制。與傳統的RSA相比&#xff0c;ECC在相同安全級別下使用更短的密鑰&#xff0c;計算效率更高&#xff0c;適用于資源受限的環境。…

力扣網編程150題:加油站(貪心解法)

一. 簡介 前面一篇文章使用暴力解法來解決力扣網150 題目&#xff1a;加油站。文章如下&#xff1a; 力扣網編程150題&#xff1a;加油站&#xff08;暴力解法&#xff09;-CSDN博客 暴力解法就是遍歷了所有元素作為起始點的可能&#xff0c;算法時間復雜度為 O(n*n)&#x…

windwos 設置redis長久密碼不生效

1、設置長久密碼redis.windows.conf 文件修改對應的設置密碼2、啟動時設置對應的加載配置文件

物聯網(IoT)領域存在多種協議

物聯網&#xff08;IoT&#xff09;領域存在多種協議&#xff0c;主要是因為不同的應用場景對通信的需求差異很大&#xff0c;包括實時性、帶寬、功耗、設備兼容性、安全性等。以下從協議多樣性的原因和你提到的具體協議&#xff08;如 dc3-driver-* 模塊&#xff09;展開說明&…

二、encoders

文章目錄一、batch_encoder (用于 BFV)1. 概述2. 數學原理3. 使用方法4. 代碼示例二、ckks_encoder (用于 CKKS)在 1. bfv_basics.cpp 中&#xff0c;我們展示了如何使用BFV方案執行非常簡單的計算。計算是在 plain_modulus 參數的模下執行的&#xff0c;并且 只使用了 BFV 明文…

數據一致性解決方案總結

數據一致性解決方案總結 我們在系統中&#xff0c;主要進行了數據冗余&#xff0c;那么就會帶來數據一致性的問題。常見的數據一致性問題有&#xff1a;數據庫主從同步延遲導致的讀數據不一致&#xff1b;數據庫主主之間數據的不一致&#xff1b;緩存和數據庫之間的數據不一致。…