【2】从零玩转OSS阿里云存储服务之Java代码操作

本文涉及的产品
对象存储 OSS,20GB 3个月
对象存储 OSS,恶意文件检测 1000次 1年
对象存储 OSS,内容安全 1000次 1年
简介: 【2】从零玩转OSS阿里云存储服务之Java代码操作

接上一个文章讲解还有一个东西忘记说

AccessKey 这玩意用来搞认证的 差不多

开通就好了…等会要用到

1735255-20200812230814175-1361287855.png

步入正题创建oss模块也就是一个项目

1735255-20200812231132735-1864665807.png

导入依赖

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
        <!-- 上传文件依赖组件 -->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.5</version>
        </dependency>
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.2.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- aliyun oss -->
        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
            <version>3.6.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.2.7.RELEASE</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <repositories>
        <repository>
            <id>aliyun</id>
            <name>aliyun Repository</name>
            <url>http://maven.aliyun.com/nexus/content/groups/public</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

创建OSSUploadUtil.java

package top.yangbuyi.utils;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.*;
import top.yangbuyi.utils.config.OSSConfig;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
 * @description: 杨不易网站:www.yby6.com
 * @program: yangbuyispringcloudparent
 * @ClassName: OSSUtils
 * @create: 2020-08-11 23:09
 * @author: yangbuyi
 * @since: JDK1.8
 * @OSSUploadUtil: 阿里云OSS文件上传工具类
 **/
public class OSSUploadUtil {
    private static OSSConfig config = null;
    /**
     *
     * @MethodName: uploadFile
     * @Description: OSS单文件上传
     * @param file
     * @param fileType 文件后缀
     * @return String 文件地址
     */
    public static String uploadFile(File file, String fileType){
      config = config==null?new OSSConfig():config;
      String fileName = config.getPicLocation()+ UUID.randomUUID().toString().toUpperCase().replace("-", "")+"."+fileType; //文件名,根据UUID来
      return putObject(file,fileType,fileName);
    }
    /**
     *
     * @MethodName: updateFile
     * @Description: 更新文件:只更新内容,不更新文件名和文件地址。
     *      (因为地址没变,可能存在浏览器原数据缓存,不能及时加载新数据,例如图片更新,请注意)
     * @param file
     * @param fileType
     * @param oldUrl
     * @return String
     */
    public static String updateFile(File file,String fileType,String oldUrl){
      String fileName = getFileName(oldUrl);
      if(fileName==null) return null;
      return putObject(file,fileType,fileName);
    }
    /**
     *
     * @MethodName: replaceFile
     * @Description: 替换文件:删除原文件并上传新文件,文件名和地址同时替换
     *      解决原数据缓存问题,只要更新了地址,就能重新加载数据)
     * @param file
     * @param fileType 文件后缀
     * @param oldUrl 需要删除的文件地址
     * @return String 文件地址
     */
    public static String replaceFile(File file,String fileType,String oldUrl){
      boolean flag = deleteFile(oldUrl);      //先删除原文件
      if(!flag){
          //更改文件的过期时间,让他到期自动删除。
      }
      return uploadFile(file, fileType);
    }
    /**
     *
     * @MethodName: deleteFile
     * @Description: 单文件删除
     * @param fileUrl 需要删除的文件url
     * @return boolean 是否删除成功
     */
    public static boolean deleteFile(String fileUrl){
      config = config==null?new OSSConfig():config;
      String bucketName = OSSUploadUtil.getBucketName(fileUrl);       //根据url获取bucketName
      String fileName = OSSUploadUtil.getFileName(fileUrl);           //根据url获取fileName
      if(bucketName==null||fileName==null) return false;
      OSSClient ossClient = null;
      try {
          ossClient = new OSSClient(config.getEndpoint(), config.getAccessKeyId(), config.getAccessKeySecret());
          GenericRequest request = new DeleteObjectsRequest(bucketName).withKey(fileName);
          ossClient.deleteObject(request);
      } catch (Exception oe) {
          oe.printStackTrace();
          return false;
      } finally {
          ossClient.shutdown();
      }
      return true;
    }
    /**
     *
     * @MethodName: batchDeleteFiles
     * @Description: 批量文件删除(较快):适用于相同endPoint和BucketName
     * @param fileUrls 需要删除的文件url集合
     * @return int 成功删除的个数
     */
    public static int deleteFile(List<String> fileUrls){
      int deleteCount = 0;    //成功删除的个数
      String bucketName = OSSUploadUtil.getBucketName(fileUrls.get(0));       //根据url获取bucketName
      List<String> fileNames = OSSUploadUtil.getFileName(fileUrls);         //根据url获取fileName
      if(bucketName==null||fileNames.size()<=0) return 0;
      OSSClient ossClient = null;
      try {
          ossClient = new OSSClient(config.getEndpoint(), config.getAccessKeyId(), config.getAccessKeySecret());
          DeleteObjectsRequest request = new DeleteObjectsRequest(bucketName).withKeys(fileNames);
          DeleteObjectsResult result = ossClient.deleteObjects(request);
          deleteCount = result.getDeletedObjects().size();
      } catch (OSSException oe) {
          oe.printStackTrace();
          throw new RuntimeException("OSS服务异常:", oe);
      } catch (ClientException ce) {
          ce.printStackTrace();
          throw new RuntimeException("OSS客户端异常:", ce);
      } finally {
          ossClient.shutdown();
      }
      return deleteCount;
    }
    /**
     *
     * @MethodName: batchDeleteFiles
     * @Description: 批量文件删除(较慢):适用于不同endPoint和BucketName
     * @param fileUrls 需要删除的文件url集合
     * @return int 成功删除的个数
     */
    public static int deleteFiles(List<String> fileUrls){
      int count = 0;
      for (String url : fileUrls) {
          if(deleteFile(url)){
            count++;
          }
      }
      return count;
    }
    /**
     *
     * @MethodName: putObject
     * @Description: 上传文件
     * @param file
     * @param fileType
     * @param fileName
     * @return String
     */
    private static String putObject(File file,String fileType,String fileName){
      config = config==null?new OSSConfig():config;
      String url = null;      //默认null
      OSSClient ossClient = null;
      try {
          ossClient = new OSSClient(config.getEndpoint(), config.getAccessKeyId(), config.getAccessKeySecret());
          InputStream input = new FileInputStream(file);
          ObjectMetadata meta = new ObjectMetadata();             // 创建上传Object的Metadata
          meta.setContentType(OSSUploadUtil.contentType(fileType));       // 设置上传内容类型
          meta.setCacheControl("no-cache");                   // 被下载时网页的缓存行为
          PutObjectRequest request = new PutObjectRequest(config.getBucketName(), fileName,input,meta);           //创建上传请求
          ossClient.putObject(request);
          url = config.getEndpoint().replaceFirst("https://","https://"+config.getBucketName()+"."+ config.getEndpoint())+"/"+fileName;       //上传成功再返回的文件路径
      } catch (OSSException oe) {
          oe.printStackTrace();
          return null;
      } catch (ClientException ce) {
          ce.printStackTrace();
          return null;
      } catch (FileNotFoundException e) {
          e.printStackTrace();
          return null;
      } finally {
          ossClient.shutdown();
      }
      return url;
    }
    /**
     *
     * @MethodName: contentType
     * @Description: 获取文件类型
     * @param FileType
     * @return String
     */
    private static String contentType(String fileType){
      fileType = fileType.toLowerCase();
      String contentType = "";
      switch (fileType) {
          case "bmp": contentType = "image/bmp";
            break;
          case "gif": contentType = "image/gif";
            break;
          case "png":
          case "jpeg":
          case "jpg": contentType = "image/jpeg";
            break;
          case "html":contentType = "text/html";
            break;
          case "txt": contentType = "text/plain";
            break;
          case "vsd": contentType = "application/vnd.visio";
            break;
          case "ppt":
          case "pptx":contentType = "application/vnd.ms-powerpoint";
            break;
          case "doc":
          case "docx":contentType = "application/msword";
            break;
          case "xml":contentType = "text/xml";
            break;
          case "mp4":contentType = "video/mp4";
            break;
          default: contentType = "application/octet-stream";
            break;
      }
      return contentType;
    }
    /**
     *
     * @MethodName: getBucketName
     * @Description: 根据url获取bucketName
     * @param fileUrl 文件url
     * @return String bucketName
     */
    private static String getBucketName(String fileUrl){
      String http = "http://";
      String https = "https://";
      int httpIndex = fileUrl.indexOf(http);
      int httpsIndex = fileUrl.indexOf(https);
      int startIndex  = 0;
      if(httpIndex==-1){
          if(httpsIndex==-1){
            return null;
          }else{
            startIndex = httpsIndex+https.length();
          }
      }else{
          startIndex = httpIndex+http.length();
      }
      int endIndex = fileUrl.indexOf(".oss-");
      return fileUrl.substring(startIndex, endIndex);
    }
    /**
     *
     * @MethodName: getFileName
     * @Description: 根据url获取fileName
     * @param fileUrl 文件url
     * @return String fileName
     */
    private static String getFileName(String fileUrl){
      String str = "yby6.com/";
      int beginIndex = fileUrl.indexOf(str);
      if(beginIndex==-1) return null;
      return fileUrl.substring(beginIndex+str.length());
    }
    /**
     *
     * @MethodName: getFileName
     * @Description: 根据url获取fileNames集合
     * @param fileUrl 文件url
     * @return List<String>  fileName集合
     */
    private static List<String> getFileName(List<String> fileUrls){
      List<String> names = new ArrayList<>();
      for (String url : fileUrls) {
          names.add(getFileName(url));
      }
      return names;
    }
}

创建Config.properties配置文件到resources下

#阿里云OSS配置
endpoint = oss-img.yby6.com # 访问节点  这里表示 uri访问的图片域名相当于一个域名
bucketName = yangbuyi-img # 你创建的Bucket
picLocation = 文件夹路径 随便填  如: yangbuyi/
accessKeyId = 第一张图片要你开通的 ID
accessKeySecret = 第一张图片要你开通的 密码

创建SystemConfig.java 处理配置文件

package top.yangbuyi.utils;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
 * @description: 杨不易网站:www.yby6.com
 * @program: yangbuyispringcloudparent
 * @ClassName: SystemConfig
 * @create: 2020-08-12 00:04
 * @author: yangbuyi
 * @since: JDK1.8
 * @SystemConfig:
 **/
public class SystemConfig {
     private static final String CONFIG_PROPERTIES="config.properties";
     public static String getConfigResource(String key) throws IOException {
         ClassLoader loader = Thread.currentThread().getContextClassLoader();
         Properties properties = new Properties();
         InputStream in = loader.getResourceAsStream(CONFIG_PROPERTIES);
         properties.load(in);
         String value = properties.getProperty(key);
         // 编码转换,从ISO-8859-1转向指定编码
         value = new String(value.getBytes("ISO-8859-1"), "UTF-8");
         in.close();
         return value;
     }
}

创建config文件夹下的OSSConfig.java

package top.yangbuyi.utils.config;
import top.yangbuyi.utils.SystemConfig;
import java.io.IOException;
/**
 * @description: 杨不易网站:www.yby6.com
 * @program: yangbuyispringcloudparent
 * @ClassName: OSSConfig
 * @create: 2020-08-12 00:04
 * @author: yangbuyi
 * @since: JDK1.8
 * @OSSConfig:
 **/
public class OSSConfig{
     private  String endpoint;       //连接区域地址
     private  String accessKeyId;    //连接keyId
     private  String accessKeySecret;    //连接秘钥
     private  String bucketName;     //需要存储的bucketName
     private  String picLocation;    //图片保存路径
     /*
     * 加载配置文件 初始化 参数
     * 也可 yml加载 看你自己  
     * */
     public OSSConfig() {
         try {
              this.endpoint = SystemConfig.getConfigResource("endpoint");
              this.bucketName = SystemConfig.getConfigResource("bucketName");
              this.picLocation = SystemConfig.getConfigResource("picLocation");
              this.accessKeyId = SystemConfig.getConfigResource("accessKeyId");
              this.accessKeySecret = SystemConfig.getConfigResource("accessKeySecret");
         } catch (IOException e) {
              e.printStackTrace();
         }
     }
     public String getEndpoint() {
         return endpoint;
     }
     public void setEndpoint(String endpoint) {
         this.endpoint = endpoint;
     }
     public String getAccessKeyId() {
         return accessKeyId;
     }
     public void setAccessKeyId(String accessKeyId) {
         this.accessKeyId = accessKeyId;
     }
     public String getAccessKeySecret() {
         return accessKeySecret;
     }
     public void setAccessKeySecret(String accessKeySecret) {
         this.accessKeySecret = accessKeySecret;
     }
     public String getBucketName() {
         return bucketName;
     }
     public void setBucketName(String bucketName) {
         this.bucketName = bucketName;
     }
     public String getPicLocation() {
         return picLocation;
     }
     public void setPicLocation(String picLocation) {
         this.picLocation = picLocation;
     }
     @Override
     public String toString() {
         return "OSSConfig{" +
               "endpoint='" + endpoint + '\'' +
               ", accessKeyId='" + accessKeyId + '\'' +
               ", accessKeySecret='" + accessKeySecret + '\'' +
               ", bucketName='" + bucketName + '\'' +
               ", picLocation='" + picLocation + '\'' +
               '}';
     }
}

在测试当中创建测试类

import org.junit.Test;
import top.yangbuyi.utils.OSSUploadUtil;
import top.yangbuyi.utils.OSSUtils;
import java.io.File;
/**
 * @description: 杨不易网站:www.yby6.com
 * @program: yangbuyispringcloudparent
 * @ClassName: two
 * @create: 2020-08-12 00:06
 * @author: yangbuyi
 * @since: JDK1.8
 * @two: OSSUploadUtil.uploadFile(File file, String fileType) //单文件上传,type:文件后缀名
 * OSSUploadUtil.updateFile(File file, String fileType, String oldUrl)//更新文件:只更新内容,不更新文件名和文件地址。
 * OSSUploadUtil.replaceFile(File file, String fileType, String oldUrl)//替换文件,删除源文件并上传新文件,文件名和地址也改变
 * OSSUploadUtil.deleteFile(List<String> fileUrls)  //删除多文件,根据问价url来自定获取其中的bucket和文件名,用于bucket和文件名可能存在不同的,循环调用deleteFile方法
 * OSSUploadUtil.deleteFile(String fileUrl) //删除单文件
 * OSSUploadUtil.deleteFiles(List<String> fileUrls)  //删除多文件,根据配置直接取删除多个文件,bucket和文件地址从配置中获取,用于多文件bucket和文件名都相同的
 **/
public class two {
     @Test
     public void t() {
         // 文件上传
         File file = new File("F:\\美女图片\\dac18dd66fbbf53ddd55563b5edecac6.jpg");
         String jpg = OSSUploadUtil.uploadFile(file, "jpg");
         System.out.println(jpg);
         // 更新 只更新 图片  不更新名称
         System.out.println("更新图片开始:" + "http://oss-img.yby6.com/github.png");
         File file2 = new File("F:\\美女图片\\httpsimg2020.cnblogs.comblog17352552020081735255-20200805220326890-2058657956.jpg");
         String jpg1 = OSSUploadUtil.updateFile(file2, "jpg", "http://oss-img.yby6.com/github.png");
         System.out.println("更新后的:" + jpg1);
         // 删除  根据url 删除图片
//       boolean b = OSSUploadUtil.deleteFile("https://yangbuyi.oss-cn-beijing.aliyuncs.com/imgBAA8AD5484D847E09BDB535765A9EEB9.jpg");
//       System.out.println(b);
     }
}

列举常用的上传

方法名

描述

OSSUploadUtil.uploadFile(File file, String fileType)

单文件上传,type:文件后缀名

OSSUploadUtil.updateFile(File file, String fileType, String oldUrl)

 更新文件:只更新内容,不更新文件名和文件地址。

OSSUploadUtil.deleteFiles(List fileUrls)

删除多文件,根据配置直接取删除多个文件,bucket和文件地址从配置中获取,用于多文件bucket和文件名都相同的

OSSUploadUtil.deleteFile(String fileUrl)

根据url删除单文件

OSSUploadUtil.replaceFile(File file, String fileType, String oldUrl)

替换文件,删除源文件并上传新文件,文件名和地址也改变

OSSUploadUtil.deleteFile(List fileUrls)

 删除多文件,根据问价url来自定获取其中的bucket和文件名,用于bucket和文件名可能存在不同的,循环调用deleteFile方法

看到这 是不是觉得 超级简单 就CV大法。。

看到这 是不是觉得 超级简单 就CV大法。。

给到的项目地址 有不同oss实现 本文章只讲了 OSSuploadUtil 对应的测试类为 two.java

测试完毕复制url访问

https://oss-yby.yby6.com/uni-app/1602927243%281%29.jpg

项目地址: https://gitee.com/yangbuyi/ossAlibaba

oss云存储完毕

相关实践学习
借助OSS搭建在线教育视频课程分享网站
本教程介绍如何基于云服务器ECS和对象存储OSS,搭建一个在线教育视频课程分享网站。
相关文章
|
1月前
|
JavaScript NoSQL Java
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
194 96
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
|
8天前
|
消息中间件 Java 应用服务中间件
JVM实战—1.Java代码的运行原理
本文介绍了Java代码的运行机制、JVM类加载机制、JVM内存区域及其作用、垃圾回收机制,并汇总了一些常见问题。
JVM实战—1.Java代码的运行原理
|
16天前
|
传感器 监控 Java
Java代码结构解析:类、方法、主函数(1分钟解剖室)
### Java代码结构简介 掌握Java代码结构如同拥有程序世界的建筑蓝图,类、方法和主函数构成“黄金三角”。类是独立的容器,承载成员变量和方法;方法实现特定功能,参数控制输入环境;主函数是程序入口。常见错误包括类名与文件名不匹配、忘记static修饰符和花括号未闭合。通过实战案例学习电商系统、游戏角色控制和物联网设备监控,理解类的作用、方法类型和主函数任务,避免典型错误,逐步提升编程能力。 **脑图速记法**:类如太空站,方法即舱段;main是发射台,static不能换;文件名对仗,括号要成双;参数是坐标,void不返航。
43 5
|
1月前
|
网络协议 Java Shell
java spring 项目若依框架启动失败,启动不了服务提示端口8080占用escription: Web server failed to start. Port 8080 was already in use. Action: Identify and stop the process that’s listening on port 8080 or configure this application to listen on another port-优雅草卓伊凡解决方案
java spring 项目若依框架启动失败,启动不了服务提示端口8080占用escription: Web server failed to start. Port 8080 was already in use. Action: Identify and stop the process that’s listening on port 8080 or configure this application to listen on another port-优雅草卓伊凡解决方案
78 7
|
2月前
|
SQL Java 数据库连接
如何在 Java 代码中使用 JSqlParser 解析复杂的 SQL 语句?
大家好,我是 V 哥。JSqlParser 是一个用于解析 SQL 语句的 Java 库,可将 SQL 解析为 Java 对象树,支持多种 SQL 类型(如 `SELECT`、`INSERT` 等)。它适用于 SQL 分析、修改、生成和验证等场景。通过 Maven 或 Gradle 安装后,可以方便地在 Java 代码中使用。
506 11
|
2月前
|
存储 分布式计算 Hadoop
基于Java的Hadoop文件处理系统:高效分布式数据解析与存储
本文介绍了如何借鉴Hadoop的设计思想,使用Java实现其核心功能MapReduce,解决海量数据处理问题。通过类比图书馆管理系统,详细解释了Hadoop的两大组件:HDFS(分布式文件系统)和MapReduce(分布式计算模型)。具体实现了单词统计任务,并扩展支持CSV和JSON格式的数据解析。为了提升性能,引入了Combiner减少中间数据传输,以及自定义Partitioner解决数据倾斜问题。最后总结了Hadoop在大数据处理中的重要性,鼓励Java开发者学习Hadoop以拓展技术边界。
77 7
|
2月前
|
JSON Java 数据挖掘
利用 Java 代码获取淘宝关键字 API 接口
在数字化商业时代,精准把握市场动态与消费者需求是企业成功的关键。淘宝作为中国最大的电商平台之一,其海量数据中蕴含丰富的商业洞察。本文介绍如何通过Java代码高效、合规地获取淘宝关键字API接口数据,帮助商家优化产品布局、制定营销策略。主要内容包括: 1. **淘宝关键字API的价值**:洞察用户需求、优化产品标题与详情、制定营销策略。 2. **获取API接口的步骤**:注册账号、申请权限、搭建Java开发环境、编写调用代码、解析响应数据。 3. **注意事项**:遵守法律法规与平台规则,处理API调用限制。 通过这些步骤,商家可以在激烈的市场竞争中脱颖而出。
|
22天前
|
存储 监控 Java
【Java并发】【线程池】带你从0-1入门线程池
欢迎来到我的技术博客!我是一名热爱编程的开发者,梦想是编写高端CRUD应用。2025年我正在沉淀中,博客更新速度加快,期待与你一起成长。 线程池是一种复用线程资源的机制,通过预先创建一定数量的线程并管理其生命周期,避免频繁创建/销毁线程带来的性能开销。它解决了线程创建成本高、资源耗尽风险、响应速度慢和任务执行缺乏管理等问题。
154 60
【Java并发】【线程池】带你从0-1入门线程池
|
11天前
|
存储 网络协议 安全
Java网络编程,多线程,IO流综合小项目一一ChatBoxes
**项目介绍**:本项目实现了一个基于TCP协议的C/S架构控制台聊天室,支持局域网内多客户端同时聊天。用户需注册并登录,用户名唯一,密码格式为字母开头加纯数字。登录后可实时聊天,服务端负责验证用户信息并转发消息。 **项目亮点**: - **C/S架构**:客户端与服务端通过TCP连接通信。 - **多线程**:采用多线程处理多个客户端的并发请求,确保实时交互。 - **IO流**:使用BufferedReader和BufferedWriter进行数据传输,确保高效稳定的通信。 - **线程安全**:通过同步代码块和锁机制保证共享数据的安全性。
63 23
|
18天前
|
Java 调度
【源码】【Java并发】【线程池】邀请您从0-1阅读ThreadPoolExecutor源码
当我们创建一个`ThreadPoolExecutor`的时候,你是否会好奇🤔,它到底发生了什么?比如:我传的拒绝策略、线程工厂是啥时候被使用的? 核心线程数是个啥?最大线程数和它又有什么关系?线程池,它是怎么调度,我们传入的线程?...不要着急,小手手点上关注、点赞、收藏。主播马上从源码的角度带你们探索神秘线程池的世界...
90 0
【源码】【Java并发】【线程池】邀请您从0-1阅读ThreadPoolExecutor源码