Spring中的异步任务(CompletableFuture版)

CompletableFuture vs Future

CompletableFuture可以回调,Future没有回调函数。

AsyncTaskConfig.java

java 复制代码
package com.xxxx.web.core.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;

/**
 * 异步任务线程池配置
 */
@Configuration
public class AsyncTaskConfig {
    @Bean
    public Executor ocrTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(2);
        executor.setQueueCapacity(500);
        executor.setThreadNamePrefix("OCR-AsyncTask-");
        executor.initialize();
        return executor;
    }
}

Application.java

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

这里主要就是配置@EnableAsync注解

OcrController.java

java 复制代码
package com.xxx.web.controller.system.api;

import com.xxx.common.core.domain.AjaxResult;
import com.xxx.system.service.ITesseractOCRService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.util.concurrent.CompletableFuture;

@Slf4j
@RestController
@RequestMapping("/api/ocr")
public class OcrController {


    @Resource
    private ITesseractOCRService iTesseractOCRService;

    @PostMapping("/testAsync")
    public AjaxResult recognizeTextAsync(@RequestParam("file") MultipartFile file) {
        log.info("开始recognizeTextAsync任务:{}", file.getOriginalFilename());
        CompletableFuture<String> ocrCompletableFuture = iTesseractOCRService.recognizeTextAsync(file);
        // 如果执行成功:
        ocrCompletableFuture.thenAccept((result) -> log.info("ocr任务结束:{}---{}", file.getOriginalFilename() ,result));
        // 如果执行异常:
        ocrCompletableFuture.exceptionally((e) -> {
            log.error("ocr任务异常结束", e);
            return null;
        });
        log.info("结束recognizeTextAsync任务:{}", file.getOriginalFilename());
        return AjaxResult.success();
    }
}

这里关键代码就是thenAccept异步任务完成正常回调和exceptionally异步任务执行异常回调。

ITesseractOCRService.java

java 复制代码
package com.xxxx.system.service;

import org.springframework.web.multipart.MultipartFile;

import java.util.concurrent.CompletableFuture;

/**
 * ocr识别服务
 */
public interface ITesseractOCRService {
    /**
     * ocr识别
     * @param multipartFile 上传文件对象
     * @return ocr识别字符串
     */
    String recognizeText(MultipartFile multipartFile);


    /**
     * ocr异步识别
     * @param multipartFile 上传文件对象
     * @return ocr识别字符串
     */
    CompletableFuture<String> recognizeTextAsync(MultipartFile multipartFile);
}

TesseractOCRServiceImpl.java

java 复制代码
package com.xxxx.system.service.impl;

import com.xxx.common.exception.ServiceException;
import com.xxx.system.service.ITesseractOCRService;
import lombok.extern.slf4j.Slf4j;
import net.sourceforge.tess4j.Tesseract;
import net.sourceforge.tess4j.TesseractException;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.concurrent.CompletableFuture;

@Service
@Slf4j
public class TesseractOCRServiceImpl implements ITesseractOCRService {
    @Resource
    private Tesseract tesseract;

    @Override
    public String recognizeText(MultipartFile multipartFile) {
        try {
            BufferedImage image = ImageIO.read(multipartFile.getInputStream());
            return tesseract.doOCR(image);
        } catch (TesseractException e) {
            log.error("OCR识别异常:{}", multipartFile.getOriginalFilename(), e);
            throw new ServiceException(String.format("OCR识别异常:%s", multipartFile.getOriginalFilename()));
        } catch (IOException e) {
            log.error("OCR读文件异常", e);
            throw new ServiceException(String.format("OCR读文件异常:%s", multipartFile.getOriginalFilename()));
        }
    }

    @Async("ocrTaskExecutor")
    @Override
    public CompletableFuture<String> recognizeTextAsync(MultipartFile multipartFile) {
        log.info("开始Ocr图片任务{}", multipartFile.getOriginalFilename());
        String results = this.recognizeText(multipartFile);
        log.info("完成Ocr图片任务{}", multipartFile.getOriginalFilename());
        return CompletableFuture.completedFuture(results);
    }
}

这里关键代码就是@Async("ocrTaskExecutor")表示方法使用ocrTaskExecutor线程池来运行;CompletableFuture.completedFuture来返回正常异常任务执行完成。

总结

CompletableFuture的回调方式,大大简化了Spring异步任务处理实现。

参考

相关推荐
独泪了无痕1 分钟前
开发利器Hutool之MapUtil的使用
java·后端
唐青枫5 分钟前
Java ThreadLocal 详解:从线程隔离到线程池实战,彻底避开脏数据与内存泄漏
java
T_Apollo5 分钟前
2.6 Java 11 版本新增特性和类
java
码农-0047 分钟前
Springboot 集成 Ehcache操作数据库显示SQL语句设置
数据库·spring boot·sql
用户37215742613514 分钟前
如何使用 Java 创建 Excel 数据透视表
java
用户31268748772018 分钟前
JVM 内存结构全链路:堆、栈、方法区、元空间
java
IT枫斗者枫哥18 分钟前
Spring Boot导入返回409,为什么第一行还是入库了?
java·spring boot·后端
xiaoqiMikko19 分钟前
JVM 线上排查实战(五):升 JDK 17 后进程起不来,十几个老 GC 参数挨个实测(完结)
java·jvm
Flynt19 分钟前
Java 27 悄悄改了 3 个默认值,我在 1 核小机器上逐个验证了一遍
java·jvm·性能优化
天天被压力20 分钟前
【Python 量化取数指南 #09】Python 拿港股通数据:港股通成交与港股财报实测
java·人工智能·python