亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

SpringBoot實現(xiàn)文件斷點續(xù)傳功能詳解

 更新時間:2025年04月02日 08:36:54   作者:風(fēng)象南  
在處理大文件傳輸或網(wǎng)絡(luò)不穩(wěn)定的情況下,文件斷點續(xù)傳功能顯得尤為重要,本文將詳細介紹如何使用Spring Boot實現(xiàn)文件的斷點續(xù)傳功能,需要的可以了解下

在處理大文件傳輸或網(wǎng)絡(luò)不穩(wěn)定的情況下,文件斷點續(xù)傳功能顯得尤為重要。本文將詳細介紹如何使用Spring Boot實現(xiàn)文件的斷點續(xù)傳功能,并提供完整的前后端代碼實現(xiàn)。

一、斷點續(xù)傳技術(shù)原理

斷點續(xù)傳的核心原理是將文件分片傳輸并記錄進度,主要包括

  • 客戶端將大文件分割成小塊逐一上傳
  • 服務(wù)端保存已上傳塊信息
  • 傳輸中斷后只需繼續(xù)傳未上傳部分

二、服務(wù)端代碼實現(xiàn)

項目依賴配置

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>2.7.18</version>
    </dependency>

    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.8.1</version>
    </dependency>
</dependencies>

application.yaml配置

file:
  upload:
    dir: D:/tmp
spring:
  servlet:
    multipart:
      enabled: true
      max-file-size: 100MB
      max-request-size: 100MB
      file-size-threshold: 2KB

文件塊信息實體類

public class FileChunkDTO {
    /**
     * 當前文件塊,從1開始
     */
    private Integer chunkNumber;
    
    /**
     * 分塊大小
     */
    private Long chunkSize;
    
    /**
     * 當前分塊大小
     */
    private Long currentChunkSize;
    
    /**
     * 總大小
     */
    private Long totalSize;
    
    /**
     * 文件標識
     */
    private String identifier;
    
    /**
     * 文件名
     */
    private String filename;
    
    /**
     * 相對路徑
     */
    private String relativePath;
    
    /**
     * 總塊數(shù)
     */
    private Integer totalChunks;

    public Integer getChunkNumber() {
        return chunkNumber;
    }

    public void setChunkNumber(Integer chunkNumber) {
        this.chunkNumber = chunkNumber;
    }

    public Long getChunkSize() {
        return chunkSize;
    }

    public void setChunkSize(Long chunkSize) {
        this.chunkSize = chunkSize;
    }

    public Long getCurrentChunkSize() {
        return currentChunkSize;
    }

    public void setCurrentChunkSize(Long currentChunkSize) {
        this.currentChunkSize = currentChunkSize;
    }

    public Long getTotalSize() {
        return totalSize;
    }

    public void setTotalSize(Long totalSize) {
        this.totalSize = totalSize;
    }

    public String getIdentifier() {
        return identifier;
    }

    public void setIdentifier(String identifier) {
        this.identifier = identifier;
    }

    public String getFilename() {
        return filename;
    }

    public void setFilename(String filename) {
        this.filename = filename;
    }

    public String getRelativePath() {
        return relativePath;
    }

    public void setRelativePath(String relativePath) {
        this.relativePath = relativePath;
    }

    public Integer getTotalChunks() {
        return totalChunks;
    }

    public void setTotalChunks(Integer totalChunks) {
        this.totalChunks = totalChunks;
    }
}

通用響應(yīng)類

public class FileUploadResponse {
    private boolean success;
    private String message;
    private Object data;

    public FileUploadResponse(boolean success, String message, Object data) {
        this.success = success;
        this.message = message;
        this.data = data;
    }

    public static FileUploadResponse success(String message, Object data) {
        return new FileUploadResponse(true, message, data);
    }
    
    public static FileUploadResponse success(String message) {
        return new FileUploadResponse(true, message, null);
    }
    
    public static FileUploadResponse error(String message) {
        return new FileUploadResponse(false, message, null);
    }

    public boolean isSuccess() {
        return success;
    }

    public void setSuccess(boolean success) {
        this.success = success;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }
}

文件上傳服務(wù)

import cn.hutool.core.io.FileUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;

@Service
public class FileUploadService {

    private static final Logger logger = LoggerFactory.getLogger(FileUploadService.class);

    @Value("${file.upload.dir}")
    private String uploadDir;

    /**
     * 檢查文件是否已上傳過
     */
    public boolean checkFileExists(FileChunkDTO chunk) {
        String storeChunkPath = uploadDir + File.separator + "chunks" + File.separator + chunk.getIdentifier() + File.separator + chunk.getChunkNumber();
        File storeChunk = new File(storeChunkPath);
        return storeChunk.exists() && chunk.getChunkSize() == storeChunk.length();
    }

    /**
     * 上傳文件塊
     */
    public FileUploadResponse uploadChunk(FileChunkDTO chunk, MultipartFile file) {
        try {
            if (file.isEmpty()) {
                return FileUploadResponse.error("文件塊為空");
            }
            
            // 創(chuàng)建塊文件目錄
            String chunkDirPath = uploadDir + File.separator + "chunks" + File.separator + chunk.getIdentifier();
            File chunkDir = new File(chunkDirPath);
            if (!chunkDir.exists()) {
                chunkDir.mkdirs();
            }
            
            // 保存分塊
            String chunkPath = chunkDirPath + File.separator + chunk.getChunkNumber();
            file.transferTo(new File(chunkPath));
            
            return FileUploadResponse.success("文件塊上傳成功");
        } catch (IOException e) {
            logger.error(e.getMessage(),e);
            return FileUploadResponse.error("文件塊上傳失敗: " + e.getMessage());
        }
    }

    /**
     * 合并文件塊
     */
    public FileUploadResponse mergeChunks(String identifier, String filename, Integer totalChunks) {
        try {
            String chunkDirPath = uploadDir + File.separator + "chunks" + File.separator + identifier;
            if(!FileUtil.exist(chunkDirPath)){
                return FileUploadResponse.error("文件合并失敗, 目錄不存在" );
            }

            File chunkDir = new File(chunkDirPath);
            // 創(chuàng)建目標文件
            String filePath = uploadDir + File.separator + filename;
            File destFile = new File(filePath);
            if (destFile.exists()) {
                destFile.delete();
            }
            // 使用RandomAccessFile合并文件塊
            try (RandomAccessFile randomAccessFile = new RandomAccessFile(destFile, "rw")) {
                byte[] buffer = new byte[1024];
                for (int i = 1; i <= totalChunks; i++) {
                    File chunk = new File(chunkDirPath + File.separator + i);
                    if (!chunk.exists()) {
                        return FileUploadResponse.error("文件塊" + i + "不存在");
                    }
                    
                    try (java.io.FileInputStream fis = new java.io.FileInputStream(chunk)) {
                        int len;
                        while ((len = fis.read(buffer)) != -1) {
                            randomAccessFile.write(buffer, 0, len);
                        }
                    }
                }
            }
            
            // 清理臨時文件塊
            FileUtil.del(chunkDir);
            
            return FileUploadResponse.success("文件合并成功", filePath);
        } catch (IOException e) {
            logger.error(e.getMessage(),e);
            return FileUploadResponse.error("文件合并失敗: " + e.getMessage());
        }
    }
}

控制器

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

@RestController
@RequestMapping("/api/upload")
@CrossOrigin // 允許跨域請求
public class FileUploadController {

    private static Logger logger = LoggerFactory.getLogger(FileDownloadService.class);

    @Autowired
    private FileUploadService fileUploadService;

    /**
     * 檢查文件或文件塊是否已存在
     */
    @GetMapping("/check")
    public ResponseEntity<Void> checkFileExists(FileChunkDTO chunk) {
        boolean exists = fileUploadService.checkFileExists(chunk);
        if (exists) {
            // 分片存在,返回 200
            return ResponseEntity.ok().build();
        } else {
            // 分片不存在,返回 404
            return ResponseEntity.notFound().build();
        }
    }

    /**
     * 上傳文件塊
     */
    @PostMapping("/chunk")
    public FileUploadResponse uploadChunk(
            @RequestParam(value = "chunkNumber") Integer chunkNumber,
            @RequestParam(value = "chunkSize") Long chunkSize,
            @RequestParam(value = "currentChunkSize") Long currentChunkSize,
            @RequestParam(value = "totalSize") Long totalSize,
            @RequestParam(value = "identifier") String identifier,
            @RequestParam(value = "filename") String filename,
            @RequestParam(value = "totalChunks") Integer totalChunks,
            @RequestParam("file") MultipartFile file) {

        String identifierName = identifier;
        // 如果 identifierName 包含逗號,取第一個值
        if (identifierName.contains(",")) {
            identifierName = identifierName.split(",")[0];
        }

        FileChunkDTO chunk = new FileChunkDTO();
        chunk.setChunkNumber(chunkNumber);
        chunk.setChunkSize(chunkSize);
        chunk.setCurrentChunkSize(currentChunkSize);
        chunk.setTotalSize(totalSize);
        chunk.setIdentifier(identifierName);
        chunk.setFilename(filename);
        chunk.setTotalChunks(totalChunks);

        return fileUploadService.uploadChunk(chunk, file);
    }

    /**
     * 合并文件塊
     */
    @PostMapping("/merge")
    public FileUploadResponse mergeChunks(
            @RequestParam("identifier") String identifier,
            @RequestParam("filename") String filename,
            @RequestParam("totalChunks") Integer totalChunks) {
        return fileUploadService.mergeChunks(identifier, filename, totalChunks);
    }
}

三、前端實現(xiàn)

完整HTML頁面

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>文件斷點續(xù)傳示例</title>
    <link  rel="external nofollow"  rel="stylesheet">
    <style>
        .upload-container, .download-container {
            margin-top: 30px;
            padding: 20px;
            border: 1px solid #ddd;
            border-radius: 5px;
        }
        .progress {
            margin-top: 10px;
            height: 25px;
        }
        .file-list {
            margin-top: 20px;
        }
        .file-item {
            padding: 10px;
            margin-bottom: 5px;
            border: 1px solid #eee;
            border-radius: 5px;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
    </style>
</head>
<body>
<div class="container">
    <h1 class="mt-4 mb-4">文件斷點續(xù)傳示例</h1>

    <!-- 上傳區(qū)域 -->
    <div class="upload-container">
        <h3>文件上傳(支持斷點續(xù)傳)</h3>
        <div class="mb-3">
            <label for="fileUpload" class="form-label">選擇文件</label>
            <input class="form-control" type="file" id="fileUpload">
        </div>
        <button id="uploadBtn" class="btn btn-primary">上傳文件</button>
        <div class="progress d-none" id="uploadProgress">
            <div class="progress-bar" role="progressbar" style="width: 0%;" id="uploadProgressBar">0%</div>
        </div>
        <div id="uploadStatus" class="mt-2"></div>
    </div>

    <div class="download-container">
        <h3>文件列表</h3>
        <button id="refreshBtn" class="btn btn-secondary mb-3">刷新文件列表</button>
        <div id="fileList" class="file-list">
            <div class="alert alert-info">點擊刷新按鈕獲取文件列表</div>
        </div>
    </div>
</div>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/spark-md5@3.0.2/spark-md5.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/resumablejs@1.1.0/resumable.min.js"></script>

<script>
    // 基礎(chǔ)配置
    const API_BASE_URL = 'http://localhost:8080/api';

    // DOM元素
    const uploadBtn = document.getElementById('uploadBtn');
    const fileUpload = document.getElementById('fileUpload');
    const uploadProgress = document.getElementById('uploadProgress');
    const uploadProgressBar = document.getElementById('uploadProgressBar');
    const uploadStatus = document.getElementById('uploadStatus');
    const refreshBtn = document.getElementById('refreshBtn');
    const fileList = document.getElementById('fileList');
    const downloadProgress = document.getElementById('downloadProgress');
    const downloadProgressBar = document.getElementById('downloadProgressBar');
    const downloadStatus = document.getElementById('downloadStatus');

    // 初始化resumable.js
    const resumable = new Resumable({
        target: `${API_BASE_URL}/upload/chunk`,
        query: {},
        chunkSize: 1 * 1024 * 1024, // 分片大小為1MB
        simultaneousUploads: 3,
        testChunks: true,
        throttleProgressCallbacks: 1,
        chunkNumberParameterName: 'chunkNumber',
        chunkSizeParameterName: 'chunkSize',
        currentChunkSizeParameterName: 'currentChunkSize',
        totalSizeParameterName: 'totalSize',
        identifierParameterName: 'identifier',
        fileNameParameterName: 'filename',
        totalChunksParameterName: 'totalChunks',
        method: 'POST',
        headers: {
            'Accept': 'application/json'
        },
        testMethod: 'GET',
        testTarget: `${API_BASE_URL}/upload/check`
    });

    // 分配事件監(jiān)聽器
    resumable.assignBrowse(fileUpload);

    // 文件添加事件 - 顯示文件名
    resumable.on('fileAdded', function(file) {
        console.log('File added:', file);
        // 顯示已選擇的文件名
        uploadStatus.innerHTML = `<div class="alert alert-info">已選擇文件: ${file.fileName} (${formatFileSize(file.size)})</div>`;

        // 顯示文件信息卡片
        const fileInfoDiv = document.createElement('div');
        fileInfoDiv.className = 'card mt-2 mb-2';
        fileInfoDiv.innerHTML = `
            <div class="card-body">
                <h5 class="card-title">文件信息</h5>
                <p class="card-text">文件名: ${file.fileName}</p>
                <p class="card-text">大小: ${formatFileSize(file.size)}</p>
                <p class="card-text">類型: ${file.file.type || '未知'}</p>
                <p class="card-text">分片數(shù): ${file.chunks.length}</p>
            </div>
        `;

        // 清除舊的文件信息
        const oldFileInfo = document.querySelector('.card');
        if (oldFileInfo) {
            oldFileInfo.remove();
        }

        // 插入到uploadStatus之前
        uploadStatus.parentNode.insertBefore(fileInfoDiv, uploadStatus);
    });

    // 格式化文件大小
    function formatFileSize(bytes) {
        if (bytes === 0) return '0 Bytes';
        const k = 1024;
        const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
        const i = Math.floor(Math.log(bytes) / Math.log(k));
        return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
    }

    // 分塊開始事件
    resumable.on('chunkingStart', function(file) {
        console.log('開始分塊:', file.fileName);
    });

    // 分塊進度事件
    resumable.on('chunkingProgress', function(file, ratio) {
        console.log('分塊進度:', Math.floor(ratio * 100) + '%');
    });

    // 分塊完成事件
    resumable.on('chunkingComplete', function(file) {
        console.log('分塊完成');
    });

    // 上傳開始事件
    resumable.on('uploadStart', function() {
        console.log('開始上傳');
        uploadStatus.innerHTML = '<div class="alert alert-info">開始上傳文件塊...</div>';
        window.mergeCalled = false;
    });

    // 上傳進度事件
    resumable.on('fileProgress', function(file) {
        const progress = Math.floor(file.progress() * 100);
        uploadProgress.classList.remove('d-none');
        uploadProgressBar.style.width = `${progress}%`;
        uploadProgressBar.textContent = `${progress}%`;

        // 顯示當前上傳塊信息
        const uploadedChunks = file.chunks.filter(chunk => chunk.status === 2).length;
        const totalChunks = file.chunks.length;
        uploadStatus.innerHTML = `<div class="alert alert-info">正在上傳: ${uploadedChunks}/${totalChunks} 塊 (${progress}%)</div>`;
    });

    // 總體進度事件
    resumable.on('progress', function() {
        console.log('總體進度:', Math.floor(resumable.progress() * 100) + '%');
    });

    // 上傳成功事件
    resumable.on('fileSuccess', function(file, response) {
        console.log('文件上傳成功,準備合并');
        const parsedResponse = JSON.parse(response);
        if (parsedResponse.success) {
            // 避免重復(fù)調(diào)用合并接口
            if (window.mergeCalled) {
                console.log('合并已經(jīng)調(diào)用過,跳過');
                return;
            }
            window.mergeCalled = true;

            uploadStatus.innerHTML = '<div class="alert alert-info">所有分塊上傳成功,正在合并文件...</div>';

            // 使用FormData發(fā)送合并請求
            const formData = new FormData();
            formData.append('identifier', file.uniqueIdentifier);
            formData.append('filename', file.fileName);
            formData.append('totalChunks', file.chunks.length);

            axios.post(`${API_BASE_URL}/upload/merge`, formData)
                .then(function(response) {
                    if (response.data.success) {
                        uploadStatus.innerHTML = `<div class="alert alert-success">文件上傳并合并成功!</div>`;
                        // 刷新文件列表
                        refreshFileList();
                    } else {
                        uploadStatus.innerHTML = `<div class="alert alert-danger">文件合并失敗: ${response.data.message}</div>`;
                    }
                })
                .catch(function(error) {
                    uploadStatus.innerHTML = `<div class="alert alert-danger">合并請求出錯: ${error.message}</div>`;
                });
        } else {
            uploadStatus.innerHTML = `<div class="alert alert-danger">上傳失敗: ${parsedResponse.message}</div>`;
        }
    });

    // 塊上傳錯誤事件
    resumable.on('chunkUploadError', function(file, chunk, message) {
        console.error('塊上傳錯誤:', chunk.offset, message);
        uploadStatus.innerHTML = `<div class="alert alert-warning">塊 ${chunk.offset+1}/${file.chunks.length} 上傳失敗,系統(tǒng)將重試</div>`;
    });

    // 上傳錯誤事件
    resumable.on('fileError', function(file, response) {
        try {
            const parsedResponse = JSON.parse(response);
            uploadStatus.innerHTML = `<div class="alert alert-danger">上傳錯誤: ${parsedResponse.message || '未知錯誤'}</div>`;
        } catch (e) {
            uploadStatus.innerHTML = `<div class="alert alert-danger">上傳錯誤: ${response || '未知錯誤'}</div>`;
        }
    });

    // 點擊上傳按鈕事件
    uploadBtn.addEventListener('click', function() {
        if (!resumable.files.length) {
            uploadStatus.innerHTML = '<div class="alert alert-warning">請先選擇文件!</div>';
            return;
        }
        uploadStatus.innerHTML = '<div class="alert alert-info">開始上傳...</div>';
        resumable.upload();
    });

    // 獲取文件列表
    function refreshFileList() {
        axios.get(`${API_BASE_URL}/download/files`)
            .then(function(response) {
                if (response.data.length > 0) {
                    let html = '';
                    response.data.forEach(function(fileName) {
                        html += `
                            <div class="file-item">
                                <span>${fileName}</span>
                            </div>
                        `;
                    });
                    fileList.innerHTML = html;
                } else {
                    fileList.innerHTML = '<div class="alert alert-info">沒有文件</div>';
                }
            })
            .catch(function(error) {
                fileList.innerHTML = `<div class="alert alert-danger">獲取文件列表失敗: ${error.message}</div>`;
            });
    }

    // 刷新按鈕事件
    refreshBtn.addEventListener('click', refreshFileList);

    // 初始加載文件列表
    document.addEventListener('DOMContentLoaded', function() {
        refreshFileList();
    });
</script>

</body>
</html>

四、核心實現(xiàn)原理詳解

文件分片:使用Resumable.js將大文件分割成多個小塊(默認1MB),每塊單獨上傳

檢查已上傳部分:上傳前先調(diào)用/api/upload/check檢查服務(wù)器已保存的分片

斷點續(xù)傳流程

  • 文件的唯一標識符由文件名和大小計算得出
  • 服務(wù)端在臨時目錄下按標識符創(chuàng)建文件夾存儲分片
  • 上傳完成后調(diào)用合并接口,服務(wù)端將分片按順序合并

文件合并:服務(wù)端使用RandomAccessFile實現(xiàn)高效文件合并

五、效果演示

上傳文件一半后觸發(fā)終止

再次上傳文件

通過請求可以看到,紅色的8個文件塊 (32-39) 檢查(check)失敗后進行了上傳(chunk),其他的已上傳 (1-31) 塊并沒有重新上傳。

最終結(jié)果

到此這篇關(guān)于SpringBoot實現(xiàn)文件斷點續(xù)傳功能詳解的文章就介紹到這了,更多相關(guān)SpringBoot文件斷點續(xù)傳內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 淺談Java內(nèi)存區(qū)域與對象創(chuàng)建過程

    淺談Java內(nèi)存區(qū)域與對象創(chuàng)建過程

    下面小編就為大家?guī)硪黄獪\談Java內(nèi)存區(qū)域與對象創(chuàng)建過程。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-07-07
  • 三步輕松實現(xiàn)Java的SM2前端加密后端解密

    三步輕松實現(xiàn)Java的SM2前端加密后端解密

    SM2算法和RSA算法都是公鑰密碼算法,SM2算法是一種更先進安全的算法,在我們國家商用密碼體系中被用來替換RSA算法,這篇文章主要給大家介紹了關(guān)于如何通過三步輕松實現(xiàn)Java的SM2前端加密后端解密的相關(guān)資料,需要的朋友可以參考下
    2024-01-01
  • java 生成文字圖片的示例代碼

    java 生成文字圖片的示例代碼

    本篇文章主要介紹了java 生成文字圖片的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-08-08
  • Java使用POI導(dǎo)出Excel(二):多個sheet

    Java使用POI導(dǎo)出Excel(二):多個sheet

    這篇文章介紹了Java使用POI導(dǎo)出Excel的方法,文中通過示例代碼介紹的非常詳細。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-10-10
  • 解決jasperreport導(dǎo)出的pdf每頁顯示的記錄太少問題

    解決jasperreport導(dǎo)出的pdf每頁顯示的記錄太少問題

    這篇文章主要介紹了解決jasperreport導(dǎo)出的pdf每頁顯示的記錄太少問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • Java實現(xiàn)Word/Pdf/TXT轉(zhuǎn)html的實例代碼

    Java實現(xiàn)Word/Pdf/TXT轉(zhuǎn)html的實例代碼

    本文主要介紹了Java實現(xiàn)Word/Pdf/TXT轉(zhuǎn)html的實例代碼,代碼簡單易懂,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-02-02
  • MyBatis工廠類封裝與簡化實現(xiàn)

    MyBatis工廠類封裝與簡化實現(xiàn)

    工廠類的目的是將對象的創(chuàng)建邏輯封裝在一個類中,以便客戶端代碼無需了解具體的實現(xiàn)細節(jié),本文主要介紹了MyBatis工廠類封裝與簡化實現(xiàn),具有一定的參考價值,感興趣的可以了解一下
    2024-01-01
  • 詳解java 單例模式及方法總結(jié)

    詳解java 單例模式及方法總結(jié)

    這篇文章主要介紹了詳解java 單例模式及方法總結(jié)的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • Java內(nèi)部類_動力節(jié)點Java學(xué)院整理

    Java內(nèi)部類_動力節(jié)點Java學(xué)院整理

    內(nèi)部類是指在一個外部類的內(nèi)部再定義一個類。下面通過本文給大家java內(nèi)部類的使用小結(jié),需要的朋友參考下吧
    2017-04-04
  • java 多線程死鎖詳解及簡單實例

    java 多線程死鎖詳解及簡單實例

    這篇文章主要介紹了java 多線程死鎖詳解及簡單實例的相關(guān)資料,需要的朋友可以參考下
    2017-01-01

最新評論