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

Spring MVC的三層架構(gòu)使用及解析

 更新時間:2025年09月26日 09:41:32   作者:祈禱蒼天賜我java之術(shù)  
SpringMVC三層架構(gòu)(Controller、Service、Mapper)實現(xiàn)職責分離,提升可維護性與擴展性,Controller處理請求,Service管理業(yè)務(wù)邏輯與事務(wù),Mapper負責數(shù)據(jù)持久化,各層獨立協(xié)作,優(yōu)化開發(fā)效率與系統(tǒng)穩(wěn)定性

一、Spring MVC 三層架構(gòu)概述

在傳統(tǒng)的 Java Web 開發(fā)中(如 Servlet+JSP),代碼往往混雜在一起:數(shù)據(jù)處理、頁面交互、業(yè)務(wù)邏輯全部寫在 Servlet 中,導(dǎo)致項目維護困難、擴展性差。

這種開發(fā)模式存在以下典型問題:

  • 單個Servlet文件可能包含數(shù)百行代碼,混合處理業(yè)務(wù)邏輯和視圖渲染
  • 數(shù)據(jù)庫操作直接嵌入業(yè)務(wù)邏輯中,難以更換數(shù)據(jù)源
  • 代碼復(fù)用率低,相似功能需要重復(fù)實現(xiàn)
  • 單元測試困難,各功能模塊耦合度過高

Spring MVC 的三層架構(gòu)正是為解決這些問題而生,通過職責拆分,將系統(tǒng)分為三個核心層級,每層專注于特定功能,既降低了耦合度,又提升了代碼的可復(fù)用性和可維護性。

這種分層架構(gòu)模式借鑒了企業(yè)級應(yīng)用開發(fā)的成熟經(jīng)驗,并針對Web應(yīng)用場景進行了優(yōu)化。

1.1 三層架構(gòu)的核心定義

Spring MVC 的三層架構(gòu)并非獨立存在,而是相互協(xié)作、自上而下的調(diào)用關(guān)系,具體包括:

表現(xiàn)層(Presentation Layer)
  • 直接與用戶交互,負責接收請求、返回響應(yīng)

主要功能包括:

  • 接收HTTP請求參數(shù)并進行基本校驗
  • 調(diào)用Service層處理業(yè)務(wù)邏輯
  • 返回響應(yīng)(頁面渲染或JSON數(shù)據(jù))
  • 核心組件:Spring MVC的Controller

典型實現(xiàn)示例:

@RestController
@RequestMapping("/users")
public class UserController {
    @Autowired
    private UserService userService;
    
    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.getUserById(id);
    }
}
業(yè)務(wù)邏輯層(Business Logic Layer,簡稱Service層)
  • 處理核心業(yè)務(wù)邏輯的中樞

主要職責包括:

  • 復(fù)雜業(yè)務(wù)規(guī)則的實現(xiàn)
  • 數(shù)據(jù)有效性校驗
  • 事務(wù)管理(通過@Transactional注解)
  • 異常處理
  • 組合多個數(shù)據(jù)操作完成業(yè)務(wù)功能

典型實現(xiàn)示例:

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;
    
    @Transactional
    @Override
    public User createUser(UserDto userDto) {
        // 業(yè)務(wù)校驗
        if(userMapper.existsByUsername(userDto.getUsername())) {
            throw new BusinessException("用戶名已存在");
        }
        
        // 數(shù)據(jù)轉(zhuǎn)換
        User user = new User();
        BeanUtils.copyProperties(userDto, user);
        
        // 持久化操作
        userMapper.insert(user);
        return user;
    }
}
持久層(Persistence Layer)
  • 專注于數(shù)據(jù)持久化操作

主要特點:

  • 只關(guān)心"如何訪問數(shù)據(jù)",不關(guān)心業(yè)務(wù)邏輯
  • 提供標準的CRUD操作方法
  • 支持多種持久化技術(shù)(JDBC、MyBatis、JPA等)

核心組件:

  • DAO(Data Access Object)或Repository
  • MyBatis的Mapper接口

典型實現(xiàn)示例:

@Mapper
public interface UserMapper {
    @Select("SELECT * FROM users WHERE id = #{id}")
    User selectById(Long id);
    
    @Insert("INSERT INTO users(username, password) VALUES(#{username}, #{password})")
    void insert(User user);
}

1.2 三層架構(gòu)的調(diào)用流程

請求接收階段

  • 用戶通過瀏覽器訪問/users/123
  • HTTP請求被Spring MVC的前端控制器DispatcherServlet攔截
  • DispatcherServlet查找所有已注冊的HandlerMapping

請求路由階段

  • HandlerMapping根據(jù)URL路徑/users/123匹配到UserController的getUser方法
  • 將路徑變量123解析為方法參數(shù)id

業(yè)務(wù)處理階段

  • Controller調(diào)用UserService的getUserById方法

Service層可能執(zhí)行以下操作:

  • 參數(shù)校驗(如ID有效性)
  • 業(yè)務(wù)規(guī)則判斷(如權(quán)限檢查)
  • 調(diào)用持久層獲取數(shù)據(jù)

數(shù)據(jù)訪問階段

  • Service調(diào)用UserMapper的selectById方法
  • MyBatis執(zhí)行SQL:SELECT * FROM users WHERE id = 123
  • 將查詢結(jié)果映射為User對象返回

響應(yīng)返回階段

  • 查詢結(jié)果沿調(diào)用鏈返回:Mapper → Service → Controller
  • Controller將User對象轉(zhuǎn)換為JSON格式
  • DispatcherServlet將響應(yīng)寫入HttpServletResponse

視圖渲染階段(可選)

如果返回的是視圖(如JSP):

  • DispatcherServlet將ModelAndView交給ViewResolver
  • ViewResolver解析視圖名稱,定位具體的JSP文件
  • 視圖引擎渲染JSP,生成HTML響應(yīng)

三層架構(gòu)交互時序圖示例

Client → DispatcherServlet → Controller → Service → Mapper → DB
                                   ↑                |
                                   |                ↓
Client ← DispatcherServlet ← Controller ← Service ← Mapper

這種分層架構(gòu)使得系統(tǒng)各部分的職責更加清晰,便于團隊協(xié)作開發(fā)、單元測試和后期維護。例如:

  • 前端開發(fā)人員只需要關(guān)注Controller層的接口定義
  • 業(yè)務(wù)分析師可以基于Service層的代碼理解業(yè)務(wù)規(guī)則
  • DBA可以優(yōu)化Mapper層的SQL語句而不影響業(yè)務(wù)邏輯

二、各層詳細拆解

2.1 表現(xiàn)層(Controller 層):用戶交互的 "入口"

表現(xiàn)層是 Spring MVC 框架中與用戶直接交互的層級,作為系統(tǒng)的"門面",負責處理 HTTP 請求和響應(yīng)。它的核心組件是 Controller 類,主要職責包括:

  1. 接收客戶端請求(解析請求參數(shù)、請求頭等)
  2. 進行基礎(chǔ)參數(shù)校驗
  3. 調(diào)用 Service 層處理業(yè)務(wù)邏輯
  4. 組裝響應(yīng)數(shù)據(jù)并返回給客戶端
  5. 處理異常情況并返回友好錯誤信息

2.1.1 核心組件與注解詳解

@Controller

  • 作用:標識一個類作為 Spring MVC 的控制器
  • 實現(xiàn)原理:Spring 會在啟動時掃描帶有該注解的類,并將其注冊為 Spring Bean
  • 配套機制:配合組件掃描注解 @ComponentScan 使用

示例:

@Controller
public class HomeController {
    // 控制器方法...
}

@RequestMapping

核心功能:

  • 路徑映射:將 HTTP 請求映射到控制器方法
  • 請求方法限定:通過 method 屬性指定處理的 HTTP 方法
  • 參數(shù)匹配:通過 params 屬性匹配特定請求參數(shù)
  • 頭部匹配:通過 headers 屬性匹配特定請求頭

高級用法:

  • 通配符支持:如 "/user/*" 可匹配 "/user/123" 等路徑
  • 類級別與方法級別組合:類級別定義基礎(chǔ)路徑,方法級別定義具體路徑
  • 媒體類型限定:通過 produces/consumes 指定處理的內(nèi)容類型

示例:

@RequestMapping(value = "/products", method = RequestMethod.GET)
public String listProducts() {...}

@GetMapping/@PostMapping

優(yōu)點:

  • 代碼更簡潔:相比 @RequestMapping(method = RequestMethod.GET) 更易讀
  • 語義更明確:直接表明處理的 HTTP 方法
  • 支持繼承:可以組合使用 @RequestMapping 的類級別注解

示例對比:

// 傳統(tǒng)方式
@RequestMapping(value = "/user", method = RequestMethod.GET)

// 簡化方式
@GetMapping("/user")

@RequestParam

主要參數(shù):

  • name/value:指定綁定的請求參數(shù)名稱
  • required:是否為必須參數(shù)(默認 true)
  • defaultValue:參數(shù)默認值

使用場景:

  • 處理查詢參數(shù):如 ?page=1&size=10
  • 處理表單數(shù)據(jù):如 application/x-www-form-urlencoded

示例:

@GetMapping("/search")
public String search(@RequestParam(name = "keyword", required = false, defaultValue = "") String keyword) {...}

@PathVariable

特點:

  • 用于 RESTful 風格的 URL 參數(shù)獲取
  • 支持正則表達式匹配路徑變量
  • 可配合 @RequestMapping 的通配符使用

示例:

@GetMapping("/users/{userId}/orders/{orderId}")
public String getOrder(@PathVariable Long userId, @PathVariable String orderId) {...}

@ResponseBody

工作機制:

  • 通過 HttpMessageConverter 將返回值轉(zhuǎn)換為指定格式
  • 常用轉(zhuǎn)換器:MappingJackson2HttpMessageConverter(JSON)
  • 可自定義轉(zhuǎn)換器處理特殊格式

典型應(yīng)用:

  • 前后端分離架構(gòu)中的 API 接口
  • AJAX 請求響應(yīng)
  • 移動端接口開發(fā)

示例:

@ResponseBody
@GetMapping("/api/user/{id}")
public User getUser(@PathVariable Long id) {...}

@RestController

組合優(yōu)勢:

  • 減少樣板代碼:無需在每個方法上添加 @ResponseBody
  • 語義更清晰:明確表示該類是純 API 控制器
  • 自動配置:默認啟用 JSON 序列化

實現(xiàn)原理:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {...}

2.1.2 示例:一個完整的 Controller 實現(xiàn)

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.Min;
import java.util.List;

@Controller
@RequestMapping("/user")
@Validated  // 啟用方法參數(shù)驗證
public class UserController {
    
    private final UserService userService;
    
    // 推薦使用構(gòu)造器注入
    public UserController(UserService userService) {
        this.userService = userService;
    }

    /**
     * 用戶列表頁面
     * @param page 頁碼(從1開始)
     * @param size 每頁條數(shù)
     * @param model 視圖模型
     * @return 視圖名稱
     */
    @GetMapping("/list")
    public String listUsers(
            @RequestParam(defaultValue = "1") @Min(1) int page,
            @RequestParam(defaultValue = "10") @Min(1) int size,
            Model model) {
        
        PageInfo<User> pageInfo = userService.getUsersByPage(page, size);
        model.addAttribute("pageInfo", pageInfo);
        return "user/list";
    }

    /**
     * 獲取用戶詳情(REST API)
     * @param userId 用戶ID
     * @return 統(tǒng)一響應(yīng)結(jié)果
     */
    @GetMapping("/{userId}")
    @ResponseBody
    public Result<User> getUserDetail(@PathVariable @Min(1) Long userId) {
        User user = userService.getUserById(userId);
        return Result.success(user);
    }

    /**
     * 創(chuàng)建新用戶
     * @param userDTO 用戶數(shù)據(jù)
     * @return 創(chuàng)建結(jié)果
     */
    @PostMapping
    @ResponseBody
    public Result<Long> createUser(@RequestBody @Valid UserDTO userDTO) {
        Long userId = userService.createUser(userDTO);
        return Result.success(userId);
    }
}

2.1.3 最佳實踐與注意事項

職責分離原則

Controller 應(yīng)保持"瘦身",僅處理:

  • 請求/響應(yīng)轉(zhuǎn)換
  • 基礎(chǔ)參數(shù)驗證
  • 異常捕獲

所有業(yè)務(wù)邏輯應(yīng)委托給 Service 層

參數(shù)校驗建議

使用 JSR-380 規(guī)范注解:

  • @NotNull/@NotEmpty/@NotBlank
  • @Size(min=, max=)
  • @Pattern(regexp=)
  • @Min/@Max

分組校驗:通過 groups 屬性實現(xiàn)不同場景的校驗規(guī)則

自定義校驗:實現(xiàn) ConstraintValidator 接口

統(tǒng)一響應(yīng)格式

推薦結(jié)構(gòu):

public class Result<T> {
    private int code;    // 狀態(tài)碼
    private String msg;  // 消息
    private T data;      // 數(shù)據(jù)
    private long timestamp = System.currentTimeMillis();
    // 構(gòu)造方法、靜態(tài)工廠方法等...
}

使用示例:

@GetMapping("/{id}")
public Result<User> getUser(@PathVariable Long id) {
    User user = userService.getUserById(id);
    return Result.success(user);
}

異常處理

推薦使用 @ControllerAdvice 統(tǒng)一處理異常

示例:

@ControllerAdvice
public class GlobalExceptionHandler {
    
    @ExceptionHandler(BusinessException.class)
    @ResponseBody
    public Result<?> handleBusinessException(BusinessException e) {
        return Result.fail(e.getErrorCode(), e.getMessage());
    }
}

性能考慮

避免在 Controller 中進行:

  • 復(fù)雜計算
  • 數(shù)據(jù)庫操作
  • 耗時 I/O 操作

使用異步處理:@Async、DeferredResult 等

安全建議

  • 對敏感參數(shù)進行過濾
  • 重要操作添加權(quán)限校驗
  • 防止 CSRF 攻擊
  • 輸入?yún)?shù)進行 XSS 防護

測試建議

  • 使用 MockMvc 進行單元測試

測試用例應(yīng)覆蓋:

  • 正常流程
  • 參數(shù)校驗失敗情況
  • 異常情況處理
  • 邊界條件

2.2 業(yè)務(wù)邏輯層(Service 層):系統(tǒng)的 "大腦"

Service 層是整個系統(tǒng)的核心業(yè)務(wù)處理中樞,負責實現(xiàn)業(yè)務(wù)規(guī)則、處理事務(wù)、協(xié)調(diào)多個持久層操作,是表現(xiàn)層與持久層之間的 "橋梁"。

它相當于應(yīng)用系統(tǒng)的"大腦",負責處理復(fù)雜的業(yè)務(wù)邏輯,確保業(yè)務(wù)流程的正確性和數(shù)據(jù)一致性。

2.2.1 核心組件與注解

@Service 注解
  • 作用:標記一個類為 Service 層組件,Spring 會自動掃描并注冊為 Bean,供 Controller 層注入
  • 使用場景:所有業(yè)務(wù)邏輯處理類都應(yīng)該使用此注解

示例

@Service
public class OrderServiceImpl implements OrderService {...}
@Transactional 注解
  • 作用:聲明事務(wù)管理,可用于類或方法上,指定事務(wù)的各種屬性

常用參數(shù)

  • propagation:事務(wù)傳播行為(如REQUIRED, REQUIRES_NEW)
  • isolation:事務(wù)隔離級別(如DEFAULT, READ_COMMITTED)
  • rollbackFor:指定哪些異常需要回滾
  • timeout:事務(wù)超時時間

示例

@Transactional(propagation = Propagation.REQUIRED, 
             isolation = Isolation.DEFAULT,
             timeout = 30,
             rollbackFor = Exception.class)
接口與實現(xiàn)類設(shè)計

優(yōu)勢

  • 面向接口編程,便于后續(xù)擴展
  • 方便進行單元測試(可mock接口)
  • 實現(xiàn)多態(tài)特性

典型結(jié)構(gòu)

├── service
│   ├── UserService.java          // 接口
│   └── impl
│       └── UserServiceImpl.java  // 實現(xiàn)類

2.2.2 示例:Service 接口與實現(xiàn)類

1. Service接口設(shè)計
/**
 * 用戶服務(wù)接口
 * 定義業(yè)務(wù)契約,不包含具體實現(xiàn)
 */
public interface UserService {

    /**
     * 查詢所有用戶
     * @return 用戶列表(可能為空列表)
     */
    List<User> getAllUsers();

    /**
     * 根據(jù)ID查詢用戶
     * @param userId 用戶ID
     * @return 用戶實體
     * @throws BusinessException 當ID無效時拋出
     */
    User getUserById(Integer userId) throws BusinessException;

    /**
     * 新增用戶
     * @param user 用戶實體
     * @return 操作是否成功
     * @throws BusinessException 當用戶名已存在等業(yè)務(wù)異常時拋出
     */
    boolean addUser(User user) throws BusinessException;
    
    /**
     * 批量導(dǎo)入用戶
     * @param users 用戶列表
     * @return 成功導(dǎo)入的數(shù)量
     */
    @Transactional
    int batchImportUsers(List<User> users);
}
2. Service實現(xiàn)類詳解
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;

/**
 * 用戶服務(wù)實現(xiàn)類
 * 實現(xiàn)具體的業(yè)務(wù)邏輯
 */
@Service // 標記為Service組件
public class UserServiceImpl implements UserService {

    // 使用@Resource或@Autowired注入持久層組件
    @Resource
    private UserMapper userMapper;
    
    @Resource
    private RoleService roleService;
    
    @Resource
    private LogService logService;

    // 簡單查詢操作通常不需要事務(wù)
    @Override
    public List<User> getAllUsers() {
        // 直接調(diào)用Mapper層方法獲取數(shù)據(jù)
        // 可添加緩存邏輯提升性能
        return userMapper.selectAll();
    }

    @Override
    public User getUserById(Integer userId) throws BusinessException {
        // 業(yè)務(wù)校驗
        if (userId == null || userId <= 0) {
            throw new BusinessException(ErrorCode.INVALID_USER_ID, "用戶ID無效");
        }
        
        // 查詢用戶
        User user = userMapper.selectById(userId);
        
        // 業(yè)務(wù)處理:如果用戶不存在
        if (user == null) {
            throw new BusinessException(ErrorCode.USER_NOT_FOUND, "用戶不存在");
        }
        
        return user;
    }

    // 需要事務(wù)管理的業(yè)務(wù)方法
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean addUser(User user) throws BusinessException {
        // 1. 參數(shù)校驗
        if (user == null || StringUtils.isEmpty(user.getUsername())) {
            throw new BusinessException(ErrorCode.INVALID_PARAM, "用戶信息不完整");
        }
        
        // 2. 業(yè)務(wù)校驗(用戶名唯一性檢查)
        User existingUser = userMapper.selectByUsername(user.getUsername());
        if (existingUser != null) {
            throw new BusinessException(ErrorCode.USERNAME_EXISTS, "用戶名已存在");
        }
        
        // 3. 設(shè)置默認值等業(yè)務(wù)處理
        user.setCreateTime(new Date());
        user.setStatus(1); // 默認激活狀態(tài)
        
        // 4. 調(diào)用Mapper層保存數(shù)據(jù)
        int rows = userMapper.insert(user);
        
        // 5. 關(guān)聯(lián)操作(如分配默認角色)
        roleService.assignDefaultRole(user.getId());
        
        // 6. 記錄操作日志(異步處理)
        logService.asyncRecordLog("USER_ADD", "新增用戶:" + user.getUsername());
        
        return rows > 0;
    }
    
    @Override
    @Transactional
    public int batchImportUsers(List<User> users) {
        int successCount = 0;
        for (User user : users) {
            try {
                if (addUser(user)) {
                    successCount++;
                }
            } catch (BusinessException e) {
                // 記錄導(dǎo)入失敗的用戶
                log.warn("導(dǎo)入用戶失敗: {}", user.getUsername(), e);
            }
        }
        return successCount;
    }
}

2.2.3 開發(fā)注意事項

職責劃分原則

  • Service層應(yīng)專注于業(yè)務(wù)邏輯處理
  • 所有數(shù)據(jù)庫操作必須通過Mapper/DAO層完成
  • 避免在Service層直接編寫SQL語句

事務(wù)管理最佳實踐

  • 事務(wù)注解應(yīng)加在Service層而非Controller層
  • 默認情況下只對RuntimeException回滾,建議明確指定rollbackFor
  • 只讀操作使用@Transactional(readOnly = true)提升性能
  • 避免在同一個類中自調(diào)用事務(wù)方法(因代理機制會失效)

異常處理規(guī)范

// 自定義業(yè)務(wù)異常示例
public class BusinessException extends RuntimeException {
    private String errorCode;
    
    public BusinessException(String errorCode, String message) {
        super(message);
        this.errorCode = errorCode;
    }
    
    // getter方法...
}
  • 使用自定義業(yè)務(wù)異常替代RuntimeException
  • 不同業(yè)務(wù)錯誤定義不同的錯誤碼
  • 在Controller層統(tǒng)一處理業(yè)務(wù)異常

性能優(yōu)化建議

  • 復(fù)雜查詢考慮添加緩存
  • 批量操作使用批量處理方法
  • 耗時操作考慮異步處理

測試考量

  • Service層應(yīng)易于單元測試
  • 使用Mock對象隔離依賴
  • 測試應(yīng)覆蓋各種業(yè)務(wù)場景和異常分支

典型業(yè)務(wù)場景處理

  • 分布式事務(wù):對于跨服務(wù)調(diào)用,考慮使用Seata等分布式事務(wù)解決方案
  • 冪等性處理:對于支付等關(guān)鍵業(yè)務(wù),需要實現(xiàn)冪等性控制
  • 業(yè)務(wù)流水號:重要業(yè)務(wù)操作應(yīng)生成唯一業(yè)務(wù)流水號便于追蹤

2.3 持久層(Mapper/DAO 層):數(shù)據(jù)的 "搬運工"

持久層作為應(yīng)用程序與數(shù)據(jù)庫之間的橋梁,專注于數(shù)據(jù)的持久化操作。其主要職責包括:

  1. 執(zhí)行基本的 CRUD 操作(Create/Read/Update/Delete)
  2. 處理數(shù)據(jù)庫事務(wù)
  3. 實現(xiàn)數(shù)據(jù)緩存(可選)
  4. 進行數(shù)據(jù)校驗(基礎(chǔ)級別)

與業(yè)務(wù)層不同,持久層不關(guān)心業(yè)務(wù)邏輯,只關(guān)注"數(shù)據(jù)如何存儲和獲取"。在現(xiàn)代 Java Web 開發(fā)中,MyBatis 已成為最流行的持久層框架之一,相比傳統(tǒng)的 JDBC 和 DAO 模式,它具有以下優(yōu)勢:

  • 簡化了數(shù)據(jù)庫操作
  • 提供自動的對象關(guān)系映射(ORM)
  • 支持動態(tài)SQL
  • 具有更好的性能

2.3.1 核心組件與配置

1. Mapper 接口

Mapper 接口是 MyBatis 的核心概念,它替代了傳統(tǒng)的 DAO 接口。與 DAO 不同:

  • 不需要編寫實現(xiàn)類
  • MyBatis 通過動態(tài)代理技術(shù)自動生成實現(xiàn)
  • 方法簽名直接對應(yīng) SQL 操作
// 典型 Mapper 接口定義
@Mapper
public interface UserMapper {
    // 查詢方法
    User selectById(Long id);
    
    // 插入方法
    int insert(User user);
    
    // 更新方法
    int update(User user);
    
    // 刪除方法
    int deleteById(Long id);
}
2. 關(guān)鍵注解
  • @Mapper:標記接口為 MyBatis Mapper 接口
  • @MapperScan:在 Spring Boot 啟動類或配置類上使用,指定 Mapper 接口的掃描路徑
  • @Param:用于多參數(shù)方法,指定參數(shù)名稱
3. XML 映射文件

XML 文件是 SQL 語句的主要存放位置,通常包含:

  • 結(jié)果映射定義(<resultMap>)
  • SQL 查詢語句(<select>)
  • 插入語句(<insert>)
  • 更新語句(<update>)
  • 刪除語句(<delete>)

2.3.2 示例詳解

1. Mapper 接口增強版
@Mapper
public interface UserMapper {
    // 基礎(chǔ)CRUD操作
    List<User> selectAll();
    User selectById(@Param("id") Long id);
    int insert(User user);
    int update(User user);
    int deleteById(@Param("id") Long id);
    
    // 分頁查詢
    List<User> selectByPage(@Param("offset") int offset, 
                           @Param("pageSize") int pageSize);
    
    // 條件查詢
    List<User> selectByCondition(@Param("condition") UserQueryCondition condition);
    
    // 批量操作
    int batchInsert(@Param("users") List<User> users);
    int batchUpdate(@Param("users") List<User> users);
}
2. XML 映射文件詳解
<?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="UserDetailResultMap" type="User">
        <id column="id" property="id"/>
        <result column="username" property="username"/>
        <result column="email" property="email"/>
        <result column="create_time" property="createTime" 
                jdbcType="TIMESTAMP"/>
        <!-- 關(guān)聯(lián)映射 -->
        <association property="department" javaType="Department">
            <id column="dept_id" property="id"/>
            <result column="dept_name" property="name"/>
        </association>
    </resultMap>

    <!-- 動態(tài)SQL查詢 -->
    <select id="selectByCondition" resultMap="UserDetailResultMap">
        SELECT u.*, d.id as dept_id, d.name as dept_name
        FROM user u
        LEFT JOIN department d ON u.dept_id = d.id
        <where>
            <if test="condition.username != null">
                AND u.username LIKE CONCAT('%', #{condition.username}, '%')
            </if>
            <if test="condition.email != null">
                AND u.email = #{condition.email}
            </if>
            <if test="condition.createTimeStart != null">
                AND u.create_time >= #{condition.createTimeStart}
            </if>
        </where>
        ORDER BY u.create_time DESC
    </select>

    <!-- 批量插入 -->
    <insert id="batchInsert">
        INSERT INTO user (username, email, create_time)
        VALUES
        <foreach collection="users" item="user" separator=",">
            (#{user.username}, #{user.email}, #{user.createTime})
        </foreach>
    </insert>
</mapper>

2.3.3 高級特性與最佳實踐

動態(tài)SQL

  • 使用<if>, <choose>, <when>, <otherwise>標簽實現(xiàn)條件判斷
  • <where>標簽自動處理WHERE子句
  • <set>標簽用于UPDATE語句

關(guān)聯(lián)查詢

  • 一對一:<association>
  • 一對多:<collection>
  • 延遲加載:配置lazyLoadingEnabled=true

性能優(yōu)化

  • 使用二級緩存(需謹慎)
  • 批量操作代替單條操作
  • 合理使用延遲加載

事務(wù)管理

  • 在Service層使用@Transactional注解
  • 配置適當?shù)氖聞?wù)傳播行為

分頁實現(xiàn)

  • 使用PageHelper插件
  • 手動實現(xiàn)分頁(limit offset)

2.3.4 常見問題解決方案

參數(shù)映射問題

  • 簡單類型參數(shù):直接使用#{param}
  • 對象參數(shù):使用#{propertyName}
  • Map參數(shù):使用#{key}
  • 多參數(shù):必須使用@Param注解

結(jié)果映射問題

  • 字段名與屬性名不一致時使用<resultMap>
  • 復(fù)雜類型使用嵌套映射
  • 使用<constructor>進行構(gòu)造函數(shù)映射

SQL注入防護

  • 永遠使用#{}而不是${}進行參數(shù)綁定
  • 對用戶輸入進行嚴格校驗

性能監(jiān)控

  • 配置SQL日志輸出
  • 使用MyBatis-Plus的性能分析插件
  • 監(jiān)控慢SQL

通過以上規(guī)范和最佳實踐,可以構(gòu)建出高效、可維護的持久層,為應(yīng)用程序提供可靠的數(shù)據(jù)訪問支持。

三、基于三層架構(gòu)搭建 Spring MVC 項目

3.1 項目結(jié)構(gòu)(Maven)

一個標準的 Spring MVC 三層架構(gòu)項目結(jié)構(gòu)如下(以 IntelliJ IDEA 為例):

src/
├── main/
│   ├── java/
│   │   └── com/
│   │       └── example/
│   │           ├── controller/       # 表現(xiàn)層(Controller)
│   │           │   └── UserController.java
│   │           ├── service/          # 業(yè)務(wù)邏輯層(Service)
│   │           │   ├── UserService.java      # 接口
│   │           │   └── impl/
│   │           │       └── UserServiceImpl.java  # 實現(xiàn)類
│   │           ├── mapper/           # 持久層(Mapper)
│   │           │   └── UserMapper.java
│   │           ├── entity/           # 實體類(對應(yīng)數(shù)據(jù)庫表)
│   │           │   └── User.java
│   │           ├── exception/        # 自定義異常
│   │           │   └── BusinessException.java
│   │           ├── config/           # Spring配置類
│   │           │   └── SpringMvcConfig.java
│   │           └── util/             # 工具類
│   │               └── Result.java   # 統(tǒng)一響應(yīng)封裝
│   ├── resources/
│   │   ├── mapper/                   # Mapper XML文件
│   │   │   └── UserMapper.xml
│   │   ├── application.properties    # 全局配置(數(shù)據(jù)庫、MyBatis等)
│   │   └── static/                   # 靜態(tài)資源(CSS、JS、圖片)
│   └── webapp/                       # Web資源
│       ├── WEB-INF/
│       │   ├── views/                # 視圖頁面(JSP/HTML)
│       │   │   └── userList.jsp
│       │   └── web.xml               # Web配置(可選,Spring Boot可省略)
└── pom.xml                           # Maven依賴

各層職責說明

  • 表現(xiàn)層(Controller):接收HTTP請求,參數(shù)校驗,調(diào)用Service并返回響應(yīng)
  • 業(yè)務(wù)邏輯層(Service):處理業(yè)務(wù)邏輯,事務(wù)控制,調(diào)用Mapper層
  • 持久層(Mapper):數(shù)據(jù)庫操作,與MyBatis框架交互
  • 實體層(Entity):定義數(shù)據(jù)模型,與數(shù)據(jù)庫表映射
  • 配置層(Config):Spring相關(guān)配置,如MVC配置、組件掃描等

3.2 核心依賴(pom.xml)

<!-- Spring MVC核心依賴 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.28</version>
</dependency>

<!-- MyBatis依賴 -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.13</version>
</dependency>

<!-- MyBatis整合Spring -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>2.1.2</version>
</dependency>

<!-- MySQL驅(qū)動 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.33</version>
    <scope>runtime</scope>
</dependency>

<!-- 數(shù)據(jù)庫連接池(HikariCP) -->
<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
    <version>5.0.1</version>
</dependency>

<!-- JSP依賴(若使用JSP視圖) -->
<dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>jsp-api</artifactId>
    <version>2.2</version>
    <scope>provided</scope>
</dependency>

<!-- lombok(簡化實體類代碼) -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.28</version>
    <optional>true</optional>
</dependency>

<!-- JSON處理(Jackson) -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.14.2</version>
</dependency>

3.3 核心配置(application.properties)

# 數(shù)據(jù)庫配置(HikariCP)
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/spring_mvc_db?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=123456

# HikariCP連接池配置
spring.datasource.hikari.maximum-pool-size=10
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.idle-timeout=300000

# MyBatis配置
# Mapper XML文件路徑
mybatis.mapper-locations=classpath:mapper/*.xml
# 實體類別名掃描包(簡化XML中的type配置)
mybatis.type-aliases-package=com.example.entity
# 開啟MyBatis日志(便于調(diào)試SQL)
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

# Spring MVC視圖解析器配置(JSP)
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp

# 靜態(tài)資源訪問配置(CSS/JS/圖片)
spring.mvc.static-path-pattern=/static/**

3.4 配置類(SpringMvcConfig.java)

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration // 標記為配置類
@EnableWebMvc // 開啟Spring MVC功能
@ComponentScan("com.example") // 掃描組件(Controller/Service等)
@MapperScan("com.example.mapper") // 掃描MyBatis Mapper接口
public class SpringMvcConfig implements WebMvcConfigurer {

    // 配置視圖解析器(JSP)
    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.jsp("/WEB-INF/views/", ".jsp");
    }

    // 配置靜態(tài)資源訪問(避免靜態(tài)資源被DispatcherServlet攔截)
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**")
                .addResourceLocations("classpath:/static/");
    }
    
    // 配置JSON消息轉(zhuǎn)換器(自動注冊Jackson)
    // Spring會自動注冊MappingJackson2HttpMessageConverter
}

3.5 功能測試:驗證三層架構(gòu)流程

3.5.1 數(shù)據(jù)庫表準備(MySQL)

CREATE DATABASE IF NOT EXISTS spring_mvc_db;
USE spring_mvc_db;

CREATE TABLE IF NOT EXISTS `user` (
    `user_id` INT PRIMARY KEY AUTO_INCREMENT COMMENT '用戶ID',
    `username` VARCHAR(50) NOT NULL UNIQUE COMMENT '用戶名',
    `nickname` VARCHAR(50) DEFAULT '' COMMENT '昵稱',
    `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '創(chuàng)建時間'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '用戶表';

-- 初始化測試數(shù)據(jù)
INSERT INTO `user`(username, nickname) VALUES
('admin', '管理員'),
('user1', '普通用戶1'),
('user2', '普通用戶2');

3.5.2 接口測試(Postman)

1. 查詢所有用戶(GET)
  • 請求地址http://localhost:8080/spring-mvc-demo/user/list
  • 請求方式:GET

預(yù)期結(jié)果

  • 返回userList.jsp頁面
  • 頁面中展示數(shù)據(jù)庫中的用戶列表
  • 頁面包含用戶ID、用戶名、昵稱和創(chuàng)建時間信息
2. 根據(jù)ID查詢用戶(GET)
  • 請求地址http://localhost:8080/spring-mvc-demo/user/1
  • 請求方式:GET
  • 請求頭Accept: application/json

預(yù)期結(jié)果

{
  "code": 200,
  "msg": "success",
  "data": {
    "userId": 1,
    "username": "admin",
    "nickname": "管理員",
    "createTime": "2024-05-20 10:30:00"
  }
}
3. 新增用戶(POST)
  • 請求地址http://localhost:8080/spring-mvc-demo/user/add
  • 請求方式:POST
  • 請求頭Content-Type: application/json

請求體

{
  "username": "newuser",
  "nickname": "新用戶"
}

預(yù)期結(jié)果

{
  "code": 200,
  "msg": "添加成功",
  "data": null
}

四、三層架構(gòu)常見問題與解決方案

4.1 表現(xiàn)層常見問題

問題 1:Controller 無法接收請求(404 錯誤)

可能原因分析:

請求路徑映射問題

  • 前端發(fā)送的請求路徑與后端@RequestMapping注解定義的路徑不匹配
  • 常見錯誤包括:大小寫不一致(如/userInfo vs /userinfo)、缺少或多余斜杠(如/api/user vs /api/user/
  • 特殊字符編碼問題(如空格應(yīng)編碼為%20

Controller 配置問題

  • 類未添加@Controller@RestController注解

類未被Spring組件掃描到,可能原因:

  • @ComponentScan配置的包路徑不正確
  • Controller類所在的包不在主啟動類的同級或子級目錄下
  • 使用了錯誤的掃描注解(如@ServletComponentScan

DispatcherServlet 配置問題

  • web.xmlurl-pattern配置為/*會攔截所有請求,包括靜態(tài)資源和JSP
  • 未正確配置靜態(tài)資源處理器
  • 缺少必要的Servlet映射配置

解決方案及實施步驟:

路徑核對

  • 使用Postman等工具直接測試Controller接口
  • 在Controller方法中添加日志輸出,確認請求是否到達
  • 檢查是否有@PathVariable參數(shù)但請求未提供

注解檢查

@RestController  // 或 @Controller
@RequestMapping("/api/users")
public class UserController {
    // 確保方法上有@RequestMapping或其派生注解
    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        // ...
    }
}

DispatcherServlet 配置

推薦配置:

<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/</url-pattern>  <!-- 而不是 /* -->
</servlet-mapping>

Spring Boot中配置靜態(tài)資源:

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/static/**")
           .addResourceLocations("classpath:/static/");
}

問題 2:參數(shù)綁定失敗(400 錯誤)

可能原因深度分析:

類型不匹配

  • 前端傳遞字符串"123",但后端使用Integer接收
  • 集合類型參數(shù)未正確格式化(如List<String>需要?names=aa&names=bb

日期格式化

常見日期格式?jīng)_突:

  • 前端:"2024-05-20"
  • 后端期望:"2024/05/20"或時間戳

時區(qū)問題(如UTC與本地時區(qū)差異)

命名不一致

  • 駝峰命名與下劃線命名轉(zhuǎn)換問題
  • 嵌套對象屬性訪問(如user.address.city

完整解決方案:

基礎(chǔ)類型處理

@GetMapping("/detail")
public Result detail(@RequestParam("user_id") Integer userId) {
    // 明確指定參數(shù)名
}

日期處理最佳實踐

@PostMapping("/schedule")
public Result createSchedule(
        @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") Date startDate,
        @RequestBody @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") Date endTime) {
    // 分別處理URL參數(shù)和JSON體中的日期
}

復(fù)雜對象綁定

// 實體類
public class User {
    @JsonProperty("user_id")  // 處理JSON字段名
    private Long userId;
    
    @RequestParam("user_name")  // 處理URL參數(shù)名
    private String userName;
}

// Controller
@PostMapping("/update")
public Result updateUser(@Valid User user) {
    // 支持混合綁定方式
}

補充技巧

全局日期格式配置(application.yml):

spring:
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8

自定義參數(shù)解析器:實現(xiàn)HandlerMethodArgumentResolver處理特殊參數(shù)類型

4.2 業(yè)務(wù)邏輯層常見問題

問題 1:事務(wù)不生效(數(shù)據(jù)提交后未回滾)

詳細原因分析:

注解位置問題

  • @Transactional注解在private/protected方法上無效
  • 注解被同類中的非事務(wù)方法調(diào)用

異常處理問題

  • 捕獲了異常但未重新拋出
  • 拋出的異常類型不是RuntimeException
  • 自定義異常未繼承RuntimeException

代理機制問題

  • 使用this.method()調(diào)用導(dǎo)致繞過Spring代理
  • 特殊場景:異步方法、synchronized方法

完整解決方案:

正確的事務(wù)配置

@Service
public class OrderService {
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public void createOrder(OrderDTO dto) throws BusinessException {
        // 業(yè)務(wù)代碼
    }
}

解決自調(diào)用問題方案

方案1:重構(gòu)代碼結(jié)構(gòu)

方案2:通過AopContext獲取代理對象

((OrderService) AopContext.currentProxy()).internalMethod();

方案3:使用@Autowired注入自身(需配合@Lazy

事務(wù)調(diào)試技巧

開啟事務(wù)日志:

logging.level.org.springframework.transaction.interceptor=DEBUG
logging.level.org.springframework.jdbc.datasource.DataSourceTransactionManager=DEBUG

問題 2:Service 層循環(huán)依賴(BeanCreationException)

典型場景分析:

直接循環(huán)依賴

@Service
class AService { @Autowired BService b; }

@Service
class BService { @Autowired AService a; }

間接循環(huán)依賴: A → B → C → A

構(gòu)造器注入導(dǎo)致的不可解循環(huán)

@Service
class AService {
    private final BService b;
    public AService(BService b) { this.b = b; }
}

進階解決方案:

架構(gòu)層面重構(gòu)

  • 提取公共邏輯到新的CommonService
  • 使用門面模式封裝相關(guān)服務(wù)

技術(shù)解決方案

// 方案1:使用setter注入 + @Lazy
@Service
class AService {
    private BService b;
    
    @Autowired
    public void setB(@Lazy BService b) { this.b = b; }
}

// 方案2:使用ApplicationContext
@Service
class BService implements ApplicationContextAware {
    private ApplicationContext context;
    
    public void someMethod() {
        AService a = context.getBean(AService.class);
    }
}

Spring Boot 2.6+ 處理

配置允許循環(huán)引用(不推薦):

spring.main.allow-circular-references=true

4.3 持久層常見問題

問題 1:Mapper 接口與 XML 不匹配(BindingException)

完整排查清單:

路徑匹配問題

  • 檢查XML文件是否在resources目錄的對應(yīng)包路徑下
  • Maven項目注意src/main/resourcessrc/main/java的目錄結(jié)構(gòu)一致性

ID匹配問題

  • 方法重載導(dǎo)致混淆
  • 泛型方法特殊處理

配置問題

  • MyBatis配置文件中<mappers>配置錯誤
  • Spring Boot中mybatis.mapper-locations配置不完整

詳細解決方案:

項目結(jié)構(gòu)規(guī)范

src/main/java
  └─com/example/mapper
        UserMapper.java
src/main/resources
  └─com/example/mapper
        UserMapper.xml

XML配置示例

<?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">
    <select id="selectById" resultType="com.example.entity.User">
        SELECT * FROM user WHERE id = #{id}
    </select>
</mapper>

Spring Boot配置

# 確保掃描到Mapper接口
mybatis.mapper-locations=classpath*:mapper/**/*.xml
# 開啟MyBatis日志
logging.level.com.example.mapper=DEBUG

問題 2:SQL 執(zhí)行報錯(SQLSyntaxErrorException)

深度排查指南:

SQL語法問題

  • 數(shù)據(jù)庫方言差異(MySQL vs Oracle)
  • 保留關(guān)鍵字沖突(如使用order作為表名)
  • 分頁語法差異

參數(shù)處理問題

  • #{}${}混用導(dǎo)致的語法錯誤
  • 參數(shù)類型不匹配(如字符串參數(shù)未加引號)

數(shù)據(jù)庫連接問題

  • 連接池配置不當
  • 數(shù)據(jù)庫版本不兼容
  • SSL連接配置錯誤

專業(yè)解決方案:

SQL調(diào)試技巧

# 打印完整執(zhí)行的SQL(包括參數(shù))
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

參數(shù)處理規(guī)范

<!-- 正確用法 -->
<select id="findUsers" resultType="User">
    SELECT * FROM user 
    WHERE username = #{name} 
    AND create_time > #{date,jdbcType=TIMESTAMP}
</select>

<!-- 動態(tài)表名用法(需確保安全) -->
<select id="selectByTable" resultType="map">
    SELECT * FROM ${tableName} 
    WHERE id = #{id}
</select>

數(shù)據(jù)庫連接配置

# 完整連接配置示例
spring.datasource.url=jdbc:mysql://localhost:3306/db?useSSL=false&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

高級技巧

使用MyBatis-Plus的SQL注入器防止SQL錯誤

配置SQL執(zhí)行超時時間:

<select id="complexQuery" timeout="30">
    <!-- 復(fù)雜查詢 -->
</select>

五、三層架構(gòu)優(yōu)化建議

5.1 代碼復(fù)用與解耦

統(tǒng)一異常處理

使用Spring提供的@ControllerAdvice+@ExceptionHandler實現(xiàn)全局異常處理機制,可以避免在各個Controller中重復(fù)編寫try-catch塊。這種集中式異常處理方式具有以下優(yōu)勢:

  1. 減少重復(fù)代碼,提高代碼整潔度
  2. 統(tǒng)一異常響應(yīng)格式,便于前端處理
  3. 可靈活分類處理不同類型的異常
@ControllerAdvice
public class GlobalExceptionHandler {
    /**
     * 處理業(yè)務(wù)異常
     * @param e 業(yè)務(wù)異常對象
     * @return 統(tǒng)一響應(yīng)結(jié)果
     */
    @ExceptionHandler(BusinessException.class)
    @ResponseBody
    public Result<?> handleBusinessException(BusinessException e) {
        log.error("業(yè)務(wù)異常:{}", e.getMessage(), e);
        return Result.fail(e.getCode(), e.getMessage());
    }

    /**
     * 處理系統(tǒng)異常
     * @param e 異常對象
     * @return 統(tǒng)一響應(yīng)結(jié)果
     */
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Result<?> handleException(Exception e) {
        log.error("系統(tǒng)異常:", e);
        return Result.fail(500, "系統(tǒng)繁忙,請稍后再試");
    }
}

通用Service/DAO層設(shè)計

通過抽取通用CRUD操作方法到基類中,可以大幅減少重復(fù)代碼。這種設(shè)計模式特別適合具有大量相似CRUD操作的系統(tǒng):

1.定義通用Mapper接口

public interface BaseMapper<T> {
    int insert(T entity);  // 插入單條記錄
    int deleteById(@Param("id") Serializable id);  // 根據(jù)主鍵刪除
    int updateById(@Param("entity") T entity);  // 根據(jù)主鍵更新
    T selectById(@Param("id") Serializable id);  // 根據(jù)主鍵查詢
    List<T> selectList(@Param("entity") T entity);  // 條件查詢列表
    Page<T> selectPage(Page<T> page, @Param("entity") T entity);  // 分頁查詢
}

2.業(yè)務(wù)Mapper繼承通用接口

public interface UserMapper extends BaseMapper<User> {
    // 自定義方法
    @Select("SELECT * FROM user WHERE username = #{username}")
    User selectByUsername(@Param("username") String username);
    
    // 復(fù)雜查詢示例
    @Select("SELECT u.* FROM user u JOIN department d ON u.dept_id = d.id WHERE d.name = #{deptName}")
    List<User> selectByDepartmentName(@Param("deptName") String deptName);
}

3.通用Service實現(xiàn)

public abstract class BaseServiceImpl<M extends BaseMapper<T>, T> implements BaseService<T> {
    @Autowired
    protected M baseMapper;

    @Override
    public boolean save(T entity) {
        return baseMapper.insert(entity) > 0;
    }

    @Override
    public boolean updateById(T entity) {
        return baseMapper.updateById(entity) > 0;
    }
    
    // 其他通用方法實現(xiàn)...
}

5.2 性能優(yōu)化

緩存設(shè)計

在Service層合理使用緩存可以顯著提升系統(tǒng)性能,特別是對于讀多寫少的場景:

緩存使用場景:

  • 高頻訪問的配置數(shù)據(jù)
  • 用戶基礎(chǔ)信息
  • 商品詳情等靜態(tài)數(shù)據(jù)
  • 計算結(jié)果緩存

緩存實現(xiàn)示例:

@Service
public class UserServiceImpl implements UserService {
    @Resource
    private RedisTemplate<String, User> redisTemplate;
    @Resource
    private UserMapper userMapper;
    
    // 緩存key前綴
    private static final String USER_CACHE_PREFIX = "user:id:";
    // 緩存過期時間(小時)
    private static final long CACHE_EXPIRE_HOURS = 1;

    @Override
    @Transactional(readOnly = true)
    public User getUserById(Integer userId) {
        String key = USER_CACHE_PREFIX + userId;
        // 1. 先查緩存
        User user = redisTemplate.opsForValue().get(key);
        if (user != null) {
            return user;
        }
        
        // 2. 緩存未命中,查數(shù)據(jù)庫
        user = userMapper.selectById(userId);
        if (user != null) {
            // 3. 設(shè)置緩存
            redisTemplate.opsForValue().set(
                key, 
                user, 
                CACHE_EXPIRE_HOURS, 
                TimeUnit.HOURS
            );
            
            // 4. 異步更新用戶訪問記錄
            CompletableFuture.runAsync(() -> 
                updateUserAccessTime(userId)
            );
        }
        return user;
    }
    
    // 緩存一致性處理
    @Override
    @CacheEvict(key = "#user.id", condition = "#user.id != null")
    public boolean updateUser(User user) {
        return userMapper.updateById(user) > 0;
    }
}

批量操作優(yōu)化

使用批量操作可以大幅減少數(shù)據(jù)庫交互次數(shù),提高性能:

1.MyBatis批量插入示例:

<!-- 批量插入用戶 -->
<insert id="batchInsert" parameterType="java.util.List">
    INSERT INTO user (username, password, nickname, create_time)
    VALUES
    <foreach collection="list" item="item" index="index" separator=",">
        (#{item.username}, #{item.password}, #{item.nickname}, 
        <choose>
            <when test="item.createTime != null">#{item.createTime}</when>
            <otherwise>NOW()</otherwise>
        </choose>)
    </foreach>
</insert>

2.批量更新示例:

@Transactional
public int batchUpdateUserStatus(List<Integer> userIds, Integer status) {
    return userMapper.batchUpdateStatus(userIds, status);
}

<!-- XML映射 -->
<update id="batchUpdateStatus">
    UPDATE user SET status = #{status} WHERE id IN
    <foreach collection="userIds" item="id" open="(" separator="," close=")">
        #{id}
    </foreach>
</update>

5.3 擴展性優(yōu)化

接口版本控制

良好的版本控制策略可以保證系統(tǒng)平滑升級:

1.URL路徑版本控制:

@RestController
@RequestMapping("/api/v1/user")
public class UserControllerV1 {
    @GetMapping("/list")
    public Result<List<User>> listUsers() {
        // V1版本實現(xiàn)
    }
}

@RestController
@RequestMapping("/api/v2/user")
public class UserControllerV2 {
    @GetMapping("/list")
    public Result<PageInfo<User>> listUsers() {
        // V2版本實現(xiàn),返回分頁數(shù)據(jù)
    }
}

2.請求頭版本控制:

@GetMapping("/user/list")
public Result<?> listUsers(@RequestHeader("X-API-Version") String version) {
    if ("2.0".equals(version)) {
        // 新版本邏輯
    } else {
        // 默認版本邏輯
    }
}

多數(shù)據(jù)源支持

使用Spring的AbstractRoutingDataSource實現(xiàn)動態(tài)數(shù)據(jù)源切換:

1.配置多數(shù)據(jù)源:

@Configuration
public class DataSourceConfig {
    @Bean
    @Primary
    @ConfigurationProperties(prefix = "spring.datasource.master")
    public DataSource masterDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DataSource slaveDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    public DataSource dynamicDataSource() {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put("master", masterDataSource());
        targetDataSources.put("slave", slaveDataSource());
        
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        dynamicDataSource.setTargetDataSources(targetDataSources);
        dynamicDataSource.setDefaultTargetDataSource(masterDataSource());
        return dynamicDataSource;
    }
}

2.動態(tài)數(shù)據(jù)源路由:

public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSourceType();
    }
}

public class DataSourceContextHolder {
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

    public static void setDataSourceType(String dataSourceType) {
        contextHolder.set(dataSourceType);
    }

    public static String getDataSourceType() {
        return contextHolder.get();
    }

    public static void clearDataSourceType() {
        contextHolder.remove();
    }
}

依賴注入優(yōu)化

使用構(gòu)造器注入可以避免循環(huán)依賴問題,提高代碼可測試性:

1.推薦做法:

@Service
public class UserServiceImpl implements UserService {
    private final UserMapper userMapper;
    private final RoleService roleService;

    @Autowired
    public UserServiceImpl(UserMapper userMapper, RoleService roleService) {
        this.userMapper = userMapper;
        this.roleService = roleService;
    }
    
    // 業(yè)務(wù)方法...
}

2.使用Lombok簡化代碼:

@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {
    private final UserMapper userMapper;
    private final RoleService roleService;
    
    // 自動生成構(gòu)造器,無需手動編寫
}

3.循環(huán)依賴解決方案:

// 使用@Lazy注解解決循環(huán)依賴
@Service
public class OrderServiceImpl implements OrderService {
    private final UserService userService;
    
    public OrderServiceImpl(@Lazy UserService userService) {
        this.userService = userService;
    }
}

總結(jié)

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • JAVA實現(xiàn)經(jīng)典游戲坦克大戰(zhàn)的示例代碼

    JAVA實現(xiàn)經(jīng)典游戲坦克大戰(zhàn)的示例代碼

    小時候大家都玩過坦克大戰(zhàn)吧,熟悉的旋律和豐富的關(guān)卡陪伴了我們一整個寒暑假。本文將通過Java+Swing實現(xiàn)這一經(jīng)典游戲,感興趣的可以學習一下
    2022-01-01
  • 詳解在springmvc中解決FastJson循環(huán)引用的問題

    詳解在springmvc中解決FastJson循環(huán)引用的問題

    本篇文章主要介紹了在springmvc中解決FastJson循環(huán)引用的問題,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-01-01
  • mac系統(tǒng)剛安裝的idea打不開的問題及解決

    mac系統(tǒng)剛安裝的idea打不開的問題及解決

    這篇文章主要介紹了mac系統(tǒng)剛安裝的idea打不開的問題及解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • SpringBoot中實現(xiàn)多線程6種方式總結(jié)

    SpringBoot中實現(xiàn)多線程6種方式總結(jié)

    這篇文章主要為大家詳細介紹了在Spring Boot中實現(xiàn)多線程的6種方式,文章的示例代碼講解詳細,大家可以結(jié)合不同場景和需求進行靈活選擇
    2025-08-08
  • Java for循環(huán)的幾種用法分析

    Java for循環(huán)的幾種用法分析

    本篇文章小編為大家介紹,Java for循環(huán)的幾種用法分析。需要的朋友參考下
    2013-04-04
  • java中的類型擦除type?erasure示例詳解

    java中的類型擦除type?erasure示例詳解

    泛型是java從JDK?5開始引入的新特性,泛型的引入可以讓我們在代碼編譯的時候就強制檢查傳入的類型,從而提升了程序的健壯度,泛型可以用在類和接口上,在集合類中非常常見,本文將會講解泛型導(dǎo)致的類型擦除
    2023-09-09
  • Retrofit+RxJava實現(xiàn)帶進度條的文件下載

    Retrofit+RxJava實現(xiàn)帶進度條的文件下載

    這篇文章主要為大家詳細介紹了Retrofit+RxJava實現(xiàn)帶進度條的文件下載,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-06-06
  • springboot項目idea熱部署的教程詳解

    springboot項目idea熱部署的教程詳解

    這篇文章主要介紹了springboot項目idea熱部署,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-08-08
  • SpringCloud hystrix服務(wù)降級學習筆記

    SpringCloud hystrix服務(wù)降級學習筆記

    什么是服務(wù)降級?當服務(wù)器壓力劇增的情況下,根據(jù)實際業(yè)務(wù)情況及流量,對一些服務(wù)和頁面有策略的不處理或換種簡單的方式處理,從而釋放服務(wù)器資源以保證核心交易正常運作或高效運作
    2022-10-10
  • Java操作IO對象流進行數(shù)據(jù)的讀寫

    Java操作IO對象流進行數(shù)據(jù)的讀寫

    這篇文章主要介紹了Java操作IO對象流進行數(shù)據(jù)的讀寫,本文通過例子逐步介紹了java如何操作IO流,和文字解析,需要的朋友可以參考下
    2021-07-07

最新評論

喀什市| 新乡市| 灵丘县| 波密县| 吉木乃县| 芒康县| 西乌珠穆沁旗| 盖州市| 沭阳县| 共和县| 仙居县| 遂溪县| 白水县| 沁源县| 新河县| 广丰县| 黄冈市| 松溪县| 旅游| 台安县| 廊坊市| 贵溪市| 观塘区| 富顺县| 图木舒克市| 邵武市| 格尔木市| 高雄市| 晋州市| 东明县| 杨浦区| 巫溪县| 弋阳县| 大关县| 栾川县| 准格尔旗| 佛坪县| 荆州市| 泾源县| 江山市| 工布江达县|