前言:由于多年在写的都是python,经常在做一些异步任务时,如导入导出报表这种,都是用celery来做异步生成表格,然后循环更新任务状态,任务结束后返回文件名或其他结果。最近某个项目采用java的springboot在写,刚好也要做些报表导入导出的事情,由此查了一下资料,捡了一些别人写好的东西,并撸了一遍车轮子,以下是相关工程代码,在此记录,下次遇到好直接搬运。

1.pom文件,在此强烈推荐阿里巴巴的easyexcel,真的好用,官网文档:https://www.yuque.com/easyexcel/doc/easyexcel

        <dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.4</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.1.4</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency>

贴个目录结构

2.开启异步任务支持和配置

SpringTaskExecutor.java

package com.src.xxx;import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;@Configuration
@EnableAsync   //开启异步任务支持
public class SpringTaskExecutor implements AsyncConfigurer {@Overridepublic Executor getAsyncExecutor() {ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();taskExecutor.setCorePoolSize(5);taskExecutor.setMaxPoolSize(10);taskExecutor.setQueueCapacity(20);taskExecutor.initialize();return taskExecutor;}@Overridepublic AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {return null;}
}

AsyncTaskConstructor.java

package com.src.xxx.task;public interface AsyncTaskConstructor {public void async();
}

AsyncTaskExecutor.java

package com.src.xxx.task;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Component
public class AsyncTaskExecutor {private static Logger LOG = LoggerFactory.getLogger(AsyncTaskExecutor.class);@Asyncpublic void executor(AsyncTaskConstructor asyncTaskGenerator, String taskInfo) {LOG.info("AsyncTaskExecutor is executing async taskController:{}", taskInfo);asyncTaskGenerator.async();}
}

AsyncTaskManager.java

package com.src.xxx.task;
import com.src.xxx.entity.Task;
import com.src.xxx.service.taskService;
import com.src.xxx.tools.DatePattern;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.util.HashMap;
import java.util.Map;/*** 异步任务管理器*/
@Component
public class AsyncTaskManager {private final taskService taskService;public AsyncTaskManager(taskService taskService) {this.taskService = taskService;}@AutowiredAsyncTaskExecutor asyncTaskExecutor;/*** 初始化任务* @param asyncTaskConstructor 异步任务构造器* @return taskInfo*/public Task submit(AsyncTaskConstructor asyncTaskConstructor, String taskId) {Task task = taskService.get_task(taskId);if(task == null){return null;}asyncTaskExecutor.executor(asyncTaskConstructor,taskId);return task;}/*** 保存任务信息*/public void setTaskInfo(Task task) {taskService.update_task(task);}/*** 获取任务信息** @param taskId 任务ID* @return*/public Task getTaskInfo(String taskId) {return taskService.get_task(taskId);}}

AsyncTaskMonitor.java

package com.src.xxx.task;import com.src.xxx.entity.Task;
import com.src.xxx.tools.DatePattern;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.util.Date;/*** 异步任务监控*/
@Component
@Aspect
public class AsyncTaskMonitor {@AutowiredAsyncTaskManager manager;private static Logger LOG = LoggerFactory.getLogger(AsyncTaskMonitor.class);@Around("execution(* com.src.hospotal.task.AsyncTaskExecutor.*(..))")public void taskHandle(ProceedingJoinPoint pjp) {//获取taskIdString taskId = pjp.getArgs()[1].toString();//获取任务信息LOG.info("AsyncTaskMonitor is monitoring async taskController:{}", taskId);try {pjp.proceed();} catch (Throwable throwable) {Task task = manager.getTaskInfo(taskId);task.setStatus(1);task.setProgress(100);task.setMsg("异步任务出现错误!");task.setInfo(throwable.getMessage());task.setUpdatetime(DatePattern.getnow());manager.setTaskInfo(task);LOG.error("AsyncTaskMonitor:async taskController {} is failed.Error info:{}", taskId, throwable.getMessage());}}
}

上面目录结构里面的代码就完了。

接下来是上面代码依赖的一些其他包的文件

entity里面的Task.java

package com.src.xxx.entity;import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiParam;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.format.annotation.DateTimeFormat;import java.util.Date;@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel
public class Task {//id,task_id,status,msg,info,progress,createtime//@ExcelProperty 这个注解表示给表头命名,不加这个注解的,会默认已字段名//@ExcelIgnore 这个注解表示,不导出该字段@ExcelProperty("记录id")@ApiModelProperty(value = "id")private Integer id;@ApiModelProperty(value = "任务ID")private String taskId;@ApiModelProperty(value = "0 进行中 1出错了 2已完成")private Integer status;@ApiModelProperty(value = "提示信息")private String msg;@ApiModelProperty(value = "返回结果原始数据,如果数据类型是json,可以取obj或自行")private String info;private Integer infoType;private Object obj;private Integer progress;@ApiParam(hidden = true)@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")private Date createtime;private String updatetime;private Integer createuserid;
}

对应的数据库表结构

CREATE TABLE `xxx`.`task`  (`id` int(11) NOT NULL AUTO_INCREMENT,`task_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '任务ID',`status` int(11) NOT NULL DEFAULT 0 COMMENT '0 进行中 1出错了 2已完成',`msg` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '提示信息',`info` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '任务返回对象信息',`progress` int(11) NOT NULL DEFAULT 0 COMMENT '进度 0到100',`info_type` int(11) NOT NULL DEFAULT 0 COMMENT '0普通字符串,1json字符串',`createtime` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`createuserid` int(11) NOT NULL DEFAULT 0 COMMENT '创建者id',`updatetime` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '最后修改时间',PRIMARY KEY (`id`) USING BTREE
) ENGINE = MyISAM CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

dao里面的taskMapper.java

package com.src.xxx.dao;import com.src.xxx.entity.Task;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import org.springframework.stereotype.Repository;import java.util.List;@Mapper
@Repository
public interface taskMapper {//task_id,status,msg,info,progress,info_type,createuserid@Insert("insert into task(task_id,status,msg,info,info_type,progress,createuserid,updatetime) values (#{taskId},#{status},#{msg},#{info},#{infoType},#{progress},#{createuserid},#{updatetime})")boolean insert_task(Task task);@Update("update task set status = #{status},msg = #{msg},info= #{info},info_type= #{infoType},progress = #{progress},updatetime= #{updatetime} where id=#{id}")boolean update_task(Task task);@Select("select * from task where task_id=#{taskId}")Task get_task(String taskId);@Select("select * from task where createuserid=#{createuserid} limit #{page},#{limit}")List<Task> get_task_list(Integer createuserid, Integer page, Integer limit);@Select("select count(*) from task where createuserid=#{createuserid} limit #{page},#{limit}")Integer count(Integer createuserid, Integer page, Integer limit);
}

service

package com.src.xxx.service;import com.src.xxx.entity.Pager;
import com.src.xxx.entity.Task;public interface taskService {//添加任务boolean insert_task(Task task);//更新任务boolean update_task(Task task);//获取某个任务Task get_task(String taskId);//获取任务列表Pager<Task> get_task_list(Integer createuserid, Integer page, Integer limit);// 不带分页的获取任务列表,为了测试List<Task> get_task(Integer createuserid);
}

entity里面有个分页的 Pager.java

package com.src.xxx.entity;import io.swagger.annotations.ApiModel;
import lombok.Data;import java.util.List;@Data
@ApiModel
public class Pager<T> {private int page;//分页起始页private int size;//每页记录数private List<T> rows;//返回的记录集合private long total;//总记录条数
}

serviceImpl中 taskServiceImpl.java

package com.src.xxx.serviceImpl;import com.src.xxx.dao.taskMapper;
import com.src.xxx.entity.Pager;
import com.src.xxx.entity.Task;
import com.src.xxx.service.taskService;
import org.springframework.stereotype.Service;import java.util.HashMap;
import java.util.List;
import java.util.Map;@Service
public class taskServiceImpl implements taskService {private final taskMapper taskMapper;public taskServiceImpl(taskMapper taskMapper) {this.taskMapper = taskMapper;}@Overridepublic boolean insert_task(Task task) {return taskMapper.insert_task(task);}@Overridepublic boolean update_task(Task task) {return taskMapper.update_task(task);}@Overridepublic Task get_task(String taskId) {return taskMapper.get_task(taskId);}@Overridepublic Pager<Task> get_task_list(Integer createuserid, Integer page, Integer limit) {Integer pageStrat = page>1 ? page-1 :0;Pager<Task> pager = new Pager<Task>();List<Task> list = taskMapper.get_task_list(createuserid,pageStrat*limit,limit);pager.setRows(list);pager.setPage(page);pager.setSize(limit);pager.setTotal(taskMapper.count(createuserid,pageStrat*limit,limit));return pager;}@Overridepublic List<Task> get_task_list2(Integer createuserid) {//为了测试return taskMapper.get_task_list(createuserid,1,999);}
}

tools里面有个DatePattern.java

package com.src.xxx.tools;import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.regex.Pattern;public class DatePattern {public static final Pattern REGEX_NORM = Pattern.compile("\\d{4}-\\d{1,2}-\\d{1,2}( \\d{1,2}:\\d{1,2}(:\\d{1,2})?)?");public static final String NORM_DATE_PATTERN = "yyyy-MM-dd";public static final String NORM_TIME_PATTERN = "HH:mm:ss";public static final String NORM_DATETIME_MINUTE_PATTERN = "yyyy-MM-dd HH:mm";public static final String NORM_DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss";public static final String NORM_DATETIME_MS_PATTERN = "yyyy-MM-dd HH:mm:ss.SSS";public static final String CHINESE_DATE_PATTERN = "yyyy年MM月dd日";public static final String PURE_DATE_PATTERN = "yyyyMMdd";public static final String PURE_TIME_PATTERN = "HHmmss";public static final String PURE_DATETIME_PATTERN = "yyyyMMddHHmmss";public static final String PURE_DATETIME_MS_PATTERN = "yyyyMMddHHmmssSSS";public static final String HTTP_DATETIME_PATTERN = "EEE, dd MMM yyyy HH:mm:ss z";public static final String JDK_DATETIME_PATTERN = "EEE MMM dd HH:mm:ss zzz yyyy";public static final String UTC_PATTERN = "yyyy-MM-dd'T'HH:mm:ss'Z'";public static final String UTC_WITH_ZONE_OFFSET_PATTERN = "yyyy-MM-dd'T'HH:mm:ssZ";public static final String UTC_MS_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";public static final String UTC_MS_WITH_ZONE_OFFSET_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";public DatePattern() {}public static String getnow(){Date dd=new Date();//格式化SimpleDateFormat sim=new SimpleDateFormat(NORM_DATETIME_PATTERN);return sim.format(dd);}}

tools里面的有个ExcelUtil.java

package com.src.xxx.tools;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.src.hospotal.entity.Task;import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;import java.util.List;
public class ExcelUtil {/*** 相对路径,文件会生成在与项目平级的目录*/private static final String EXCEL_SUFFIX = ".xls";private static String FILE_PATH(){String UPLOAD_PATH = "C:/upload/excel/";if(System.getProperty("os.name").toLowerCase().indexOf("linux")>=0){UPLOAD_PATH = "/upload/excel/";}//获取文件上传的根目录 C:\Users\wanghao/upload/imgreturn  UPLOAD_PATH;}/*** 在指定位置生成excel文件** @param fileName 文件路径* @param clazz    导出数据类型* @param list     导出数据* @param <T>      可以不加这个也行,但是会有警告,看起来不舒服*/public static <T> void writeToExcel(String fileName, Class<T> clazz, List<T> list) {ExcelWriter excelWriter = EasyExcel.write(fileName).build();WriteSheet sheet = EasyExcel.writerSheet().head(clazz).build();excelWriter.write(list, sheet);excelWriter.finish();}/*** 生成excel文件** @param fileName* @param clazz* @param list* @param <T>* @return*/public static <T> File create_Excel(String fileName, Class<T> clazz, List<T> list) {System.out.println(generateFilePath());if (!generateFilePath()) {return null;}String fileFullName = FILE_PATH() + fileName + EXCEL_SUFFIX;//System.out.println(fileFullName);ExcelUtil.writeToExcel(fileFullName, clazz, list);return new File(fileFullName);}/*** 删除文件** @param file* @return*/public boolean deleteFile(File file) {if (file != null) {return file.delete();}return true;}private static boolean generateFilePath() {File path = new File(FILE_PATH());//System.out.println(path);if (path.exists()) {return true;} else {return path.mkdirs();}}public static void main(String[] args) {List<Task> list = new ArrayList<Task>();for (int i = 0; i < 10; i++) {Task data = new Task();data.setMsg("字符串" + i);data.setUpdatetime(new Date().toString());list.add(data);}System.out.println(list);File f = create_Excel("11", Task.class, list);System.out.println(f);}
}

控制器

taskController.java
package com.src.xxx.controller;import com.src.xxx.entity.*;
import com.src.xxx.service.taskService;
import com.src.xxx.task.AsyncTaskManager;
import com.src.xxx.tools.CodeUtil;
import com.src.xxx.tools.DatePattern;
import com.src.xxx.tools.Result;
import com.src.xxx.tools.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.System;
import java.util.Date;
import java.util.List;
import java.util.UUID;import static com.src.hospotal.tools.ExcelUtil.create_Excel;@RestController
@RequestMapping("/api/task")
@Api(tags="异步任务")
public class taskController {private final taskService taskService;//注入异步任务管理器private final AsyncTaskManager asyncTaskManager;@Autowiredpublic taskController(taskService taskService, AsyncTaskManager asyncTaskManager) {this.taskService = taskService;this.asyncTaskManager = asyncTaskManager;}@ApiOperation(value="导出任务记录报表")@GetMapping("/get_task_excel")public Result get_task_excel(@RequestParam(required = false) Integer createuserid, HttpServletRequest request) {//调用任务管理器中的submit去提交一个异步任务String taskId = UUID.randomUUID().toString();Task task = new Task();task.setTaskId(taskId);task.setStatus(0);task.setMsg("开始执行任务!");task.setInfo("");task.setInfoType(0);task.setProgress(0);task.setCreateuserid(createuserid);task.setUpdatetime(DatePattern.getnow());if(taskService.insert_task(task)){Task task1 = asyncTaskManager.submit(() -> {System.out.println("_____start_____");System.out.println(taskId);Task asynctask = taskService.get_task(taskId);//获取数据列表,List<Task> lists = taskService.get_task_list2(createuserid);asynctask.setMsg("正在生成文件!");asynctask.setProgress(30);taskService.update_task(asynctask);File f = create_Excel(taskId, Blood.class, lists);if(f != null){asynctask.setInfo(f.getName());asynctask.setStatus(2);asynctask.setMsg("文件已生成!");}else{asynctask.setStatus(1);asynctask.setMsg("文件生成失败!");}asynctask.setProgress(100);taskService.update_task(asynctask);System.out.println("_____end_____");}, taskId);if(task1 == null){return R.error("异步任务创建失败!");}return R.success(task1);};return R.error("异步任务创建失败!");}@ApiOperation(value="获取任务列表")@GetMapping("/get_task_list")public Result<Pager<Task>> get_task_list(@RequestParam Integer page,@RequestParam Integer size,HttpServletRequest request){Integer createuserid = 1;Pager<Task> res = taskService.get_task_list(page,size,createuserid);return R.success(res);}// @RequestMapping(value = "/start_task", method = RequestMethod.GET)public Result startAsyncTask() {//调用任务管理器中的submit去提交一个异步任务String taskId = UUID.randomUUID().toString();Task task = new Task();task.setTaskId(taskId);task.setStatus(0);task.setMsg("开始执行任务!");task.setInfo("");task.setInfoType(0);task.setProgress(0);task.setCreateuserid(1);task.setUpdatetime(DatePattern.getnow());if(taskService.insert_task(task)){Task task1 = asyncTaskManager.submit(() -> {System.out.println("_____start_____");try {//模拟异步,睡眠6秒Thread.sleep(30000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(taskId);System.out.println("_____end_____");}, taskId);if(task1 == null){return R.error("异步任务创建失败!");}return R.success(task1);};return R.error("异步任务创建失败!");}@RequestMapping(value = "/get_task", method = RequestMethod.GET)public Result getTaskStatus(@RequestParam("taskId") String taskId) {return R.success(asyncTaskManager.getTaskInfo(taskId));}@GetMapping("/{filename}")@ApiOperation(value="读取excel")public void getexcel(@PathVariable(name = "filename") String filename, HttpServletRequest request, HttpServletResponse response) throws Exception {response.setDateHeader("Expires", 0);response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");response.addHeader("Cache-Control", "post-check=0, pre-check=0");response.setHeader("Pragma", "no-cache");BufferedInputStream bis = null;OutputStream out = null;try{InputStream inputStream = CodeUtil.getExcel(filename);out = response.getOutputStream();bis = new BufferedInputStream(inputStream);byte[] buff = new byte[1024 * 2];int count = -1;out = response.getOutputStream();   //直接下载导出while ((count = bis.read(buff)) != -1){out.write(buff, 0, count);}out.flush();out.close();} catch (IOException e){} finally{if (out != null){try{out.close();} catch (IOException e){e.printStackTrace();}}}}
}

tools里面的Result.java就是一个

package com.src.xxx.tools;import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;@ApiModel(value="通用返回结构体",description = "http请求返回的最外层对象")
public class Result<T> {@ApiModelProperty(value="错误码0成功1失败401不是超级管理员",name="code")private Integer code;@ApiModelProperty(value="提示信息",name="msg")private String msg;@ApiModelProperty(value="具体的内容",name="data")private T data;public Integer getCode() {return code;}public void setCode(Integer code) {this.code = code;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}public T getData() {return data;}public void setData(T data) {this.data = data;}
}

R.java 就是定一个的一个常用方法之类的工具

package com.src.xxx.tools;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.DigestUtils;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;import static org.springframework.web.context.request.RequestContextHolder.getRequestAttributes;@Slf4j
public class R {public static Result success(Object object) {Result result = new Result();result.setCode(0);result.setMsg("success");result.setData(object);return result;}public static Result notadmin() {Result result = new Result();result.setCode(401);result.setMsg("无权限");return result;}public static Result success() {return success(null);}public static Result error(String msg) {Result result = new Result();result.setCode(1);result.setMsg(msg);return result;}}

主要代码如上,其中一些业务逻辑相关的例如登录验证这些都去掉了。临时把导出的数据表改成了task的表。

见笑了。

springboot异步任务带自定义返回结果和异步任务查看。相关推荐

  1. delphi 异步 调用 带参数_Dubbo 关于同步/异步调用的几种方式

    我们知道,Dubbo 缺省协议采用单一长连接,底层实现是 Netty 的 NIO 异步通讯机制:基于这种机制,Dubbo 实现了以下几种调用方式: 同步调用 异步调用 参数回调 事件通知 同步调用 同 ...

  2. .NET中的异步编程(一)-为什么需要异步

    在2010年的PDC上,微软发布了Visual Studio Async CTP,大大地降低了异步编程的难度,让我们可以像写同步的方法那样去编写异步代码.Async CTP也在社区里掀起了不小的波澜. ...

  3. SpringBoot异步任务, 以及带返回值的异步任务(@Async 不起作用的原因)

    第一部分: 无返回值异步任务 当没有加入异步任务的时候,我们创建一个service ,里面的方法需要等待3秒才能完成, controller层写一个测试方法调用时间返回的接口, 直接调用, 下面是se ...

  4. springboot 的异步任务 :无返回值 和有返回值

    在想要异步执行的方法上加上@Async注解,在controller上加上@EnableAsync,即可. 注:这里的异步方法,只能在本类之外调用,在本类调用是无效的. 无返回值的异步任务 servic ...

  5. springboot控制接口返回的字段_SpringBoot实战:SpringBoot之Rest Full接口自定义返回数据类型(ResponseBodyAdvice)...

    我们在日常开发的过程中,经常会要求统一返回数据格式.如要求统一访问格式为 { "success": 请求是否成功, "message": 请求消息, " ...

  6. html中的异步请求数据格式,解决layui中table异步数据请求不支持自定义返回数据格式的问题...

    使用版本 layui-v2.3.0 修改: 打开layui中table.js源码 在 Class.prototype.pullData 这个方法定义内部 //获得数据 Class.prototype. ...

  7. springboot 自定义返回值处理器HandlerMethodReturnValueHandler

    WEB开发中有这样的需求: 返回给前台的数据需要有统一格式.但是在controller的每个mapping中手写包装很是麻烦,所以可以自定义返回值处理器进行结果包装. 文章目录 返回给前台的消息格式 ...

  8. 一文带你彻底了解Java异步

    随着RxJava.Reactor等异步框架的流行,异步编程受到了越来越多的关注,尤其是在IO密集型的业务场景中,相比传统的同步开发模式,异步编程的优势越来越明显. 那到底什么是异步编程?异步化真正的好 ...

  9. return error怎么定义_SpringBoot 系列 web 篇之自定义返回 Http Code 的 n 种姿势

    200105-SpringBoot 系列 web 篇之自定义返回 Http Code 的 n 种姿势 虽然 http 的提供了一整套完整.定义明确的状态码,但实际的业务支持中,后端并不总会遵守这套规则 ...

最新文章

  1. ajax是tcp连接吗,基于微型TCP/IP协议与AJAX的动态Web服务器设计
  2. ADO.net 中数据库连接方式
  3. html文件上传数量限制,使用HTML中的input上传文件最多可以上传多少张?
  4. DevC++最新汉化版(支持C++11)
  5. 【Android-NCNN-Vulkan】ncnn-vulkan load param model 速度慢
  6. BundleFusion那些事儿
  7. python实时连接oracle_Python连接Oracle
  8. C#JsonConvert.DeserializeObject反序列化json字符
  9. 【小夕精选】YJango 7分钟带你领略你未曾想过的线性代数+微积分
  10. iOS 自带二维码扫描功能的实现
  11. python21天打卡Day8-string,int互转
  12. 现控笔记(三):状态空间表达式的解
  13. MySQL架构体系(从一条语句出发了解MySQL各部分的作用)
  14. eclipse打断点的调试
  15. 软件工程需求分析文档模板
  16. OpenJudge NOI 2.1 1813:熄灯问题
  17. 数字 IC 技能拓展(18)如何快速上手 FPGA 开发板呢
  18. Android 在mac上显示手机屏幕 MAC 投影 安卓手机
  19. css层叠样式表——css基础介绍
  20. C#学习之面象对象继承练习(二)

热门文章

  1. word文档合并和查找替换使用通配符
  2. 自闭症婴幼儿对人脸的注意偏好
  3. uni-app 实现自定义拍摄头像功能
  4. 辽宁2009对口计算机试题文档之家,2011年对口高考计算机试题
  5. IContact接口对应的字段意思
  6. jsoup api 用法
  7. Tesla T4显卡安装及显卡自带显示屏蔽设置
  8. 社交媒体图标设计欣赏(可下载)
  9. 电信 IPRAN 设备组网方案_面向产业互联网业务承载网解决方案探讨
  10. python学习之二次方程求解