SpringBoot 自定義starter 添加第三方組件(阿里雲oss爲例子)

starter可以理解爲一個可拔插式的插件,例如,你想使用jdbc插件,那麼可以使用spring-boot-starter-jdbc(官方);如果想使用mongodb(官方),可以使用spring-boot-starter-data-mongodb(官方)。
當然我們也可以自定義starter,使其變成一個組件, 例如將微信支付,支付寶支付,阿里雲oss等常見的第三方工具封裝,然後給他人使用,廢話不多說,直接上步驟

我們以阿里雲oss(雲儲存) 爲例 (博主這裏用的是IDEA)

1.創建一個空項目 ( idea 的 file >> project)

在這裏插入圖片描述
創建好之後,

2 .在空項目裏面創建一個新Module, 選擇Maven,這一塊(子項目)稱爲(啓動器模塊)

大家的取名(groupId/artifactId)隨意

也可以參考 我的pom.xml


<groupId>com.expansion</groupId>
<artifactId>expansion-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>

3 .在空項目裏面再創建一個新Module, 選擇Spring Initializr,這一塊(子項目)稱爲(自動配置模塊)

大家的取名(groupId/artifactId)隨意

也可以參考 我的pom.xml


<groupId>com.example.starter</groupId>
<artifactId>expansion-spring-boot-starter-aliyun</artifactId>
<version>0.0.1-SNAPSHOT</version>

在這個子模塊裏,可以把 關於test的依賴和test包以及項目的啓動類都刪掉,這裏用不到

4 .在你(啓動器模塊)的pom文件加上(自動配置模塊)的依賴

 <!--啓動器-->
    <dependencies>
        <!--引入自動配置模塊-->
        <dependency>
            <groupId>com.example.starter</groupId>
            <artifactId>expansion-spring-boot-starter-aliyun</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>

5 .這裏就在(自動配置模塊)開始操作阿里雲Oss

  • 5.1
    引入阿里雲oss官方依賴:
 <!-- aliyunoss-->
        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
            <version>2.8.3</version>
        </dependency>
  • 5.2
    在你的項目中創建 oss參數實體類 : OSSClientConstants
    以下是OSSClientConstants代碼:

    ps :
    這各類中只有屬性和對應的get/set方法
    @ConfigurationProperties用於指定配置,注意, prefix 不能出現大寫

package com.expansion.starter.aliyunoss;

import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * @Auther: lj
 * @Date: 2019/4/1 0001 16:18
 */
@ConfigurationProperties(prefix = "expansion.aliyun-oss") //prefix 中不能出現大寫
public class OSSClientConstants {

    //阿里雲API的外網域名
    private String endPoint;
    //阿里雲API的密鑰Access Key ID
    private String accessKeyId;
    //阿里雲API的密鑰Access Key Secret
    private String accessKeySecret;
    //阿里雲OSS文件夾
    private String backetName;

    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 getBacketName() {
        return backetName;
    }

    public String getEndPoint() {
        return endPoint;
    }

    public void setEndPoint(String endPoint) {
        this.endPoint = endPoint;
    }

    public void setBacketName(String backetName) {
        this.backetName = backetName;
    }
}
  • 5.3
    OSSClientConstants創建完畢後在創建 操作oss文件操作類 : AliyunOSSUtil
    PS:
    這裏就只需要將 OSSClientConstants 設置成 AliyunOSSUtil 的 屬性,並添加s/g構造方法
在這裏插入代碼片package com.expansion.starter.aliyunoss;
import java.io.*;  
import java.net.URL;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;

import com.aliyun.oss.OSSClient;
import com.aliyun.oss.model.Bucket;
import com.aliyun.oss.model.DeleteObjectsRequest;
import com.aliyun.oss.model.DeleteObjectsResult;
import com.aliyun.oss.model.OSSObject;
import com.aliyun.oss.model.ObjectMetadata;  
import com.aliyun.oss.model.PutObjectResult;
import org.springframework.util.StringUtils;


/**
 * 阿里雲oss文件操作
 * 支持圖片,ppt,word等常見格式的文件上傳
 * @author  lj
 *
 */
public class AliyunOSSUtil {
	//日誌
	public static final Logger LOGGER = LoggerFactory.getLogger(AliyunOSSUtil.class);

	//oss參數實體類
    private static OSSClientConstants ossEntity;

    public OSSClientConstants getOssEntity() {
        return ossEntity;
    }

    public void setOssEntity(OSSClientConstants ossEntity) {
        this.ossEntity = ossEntity;
    }

    /**
     * 獲取阿里雲OSS客戶端對象 
     * @return ossClient 
     */  
    static  OSSClient getOSSClient() {  
        return new OSSClient(ossEntity.getEndPoint(), ossEntity.getAccessKeyId(), ossEntity.getAccessKeySecret());
    }  
     
    /** 
     * 創建空間 
     * @param ossClient OSS連接 
     * @param bucketName 存儲空間 
     * @return String
     */  
    public static String createBucketName(OSSClient ossClient, String bucketName) {  
        //存儲空間  
        final String bucketNames = bucketName;  
        if (!ossClient.doesBucketExist(bucketName)) {  
            //創建存儲空間  
            Bucket bucket = ossClient.createBucket(bucketName);  
            LOGGER.info("創建存儲空間成功");  
            return bucket.getName();  
        }  
        return bucketNames;  
    }  
    
    /** 
     * 創建文件夾 
     * @param ossClient oss連接 
     * @param bucketName 存儲空間 
     * @param folder  
     * @return  文件夾名 
     */  
    public static String createFolder(OSSClient ossClient, String bucketName, String folder) {  
        //文件夾名   
        final String keySuffixWithSlash = folder;  
        //判斷文件夾是否存在,不存在則創建  
        if (!ossClient.doesObjectExist(bucketName, keySuffixWithSlash)) {  
            //創建文件夾  
            ossClient.putObject(bucketName, keySuffixWithSlash, new ByteArrayInputStream(new byte[0]));  
            LOGGER.info("創建文件夾成功");  
            //得到文件夾名  
            OSSObject object = ossClient.getObject(bucketName, keySuffixWithSlash);  
            String fileDir = object.getKey();  
            return fileDir;  
        }  
        return keySuffixWithSlash;  
    }  
   
    /** 
     * 上傳文件至OSS 
     * @param ossClient  oss連接 
     * @param file 上傳文件(文件全路徑) 
     * @param bucketName  存儲空間 
     * @param folder 模擬文件夾名 
     * @return String 返回的唯一MD5數字簽名 
     * */  
     static  String uploadObject2OSS(OSSClient ossClient, File file, String bucketName, String folder) {  
        String resultStr = null;  
        try {  
            //以輸入流的形式上傳文件  
            InputStream is = new FileInputStream(file);  
            //文件名  
            String fileName = file.getName();   
            //文件大小  
            Long fileSize = file.length();   
            //創建上傳Object的Metadata    
            ObjectMetadata metadata = new ObjectMetadata();  
            //上傳的文件的長度  
            metadata.setContentLength(is.available());    
            //指定該Object被下載時的網頁的緩存行爲  
            metadata.setCacheControl("no-cache");   
            //指定該Object下設置Header  
            metadata.setHeader("Pragma", "no-cache");    
            //指定該Object被下載時的內容編碼格式  
            metadata.setContentEncoding("utf-8");    
            //文件的MIME,定義文件的類型及網頁編碼,決定瀏覽器將以什麼形式、什麼編碼讀取文件。如果用戶沒有指定則根據Key或文件名的擴展名生成,  
            //如果沒有擴展名則填默認值application/octet-stream  
            metadata.setContentType(getContentType(fileName));    
            //指定該Object被下載時的名稱(指示MINME用戶代理如何顯示附加的文件,打開或下載,及文件名稱)  
            metadata.setContentDisposition("filename/filesize=" + fileName + "/" + fileSize + "Byte.");    
            //上傳文件   (上傳文件流的形式)  
            PutObjectResult putResult = ossClient.putObject(bucketName, folder + fileName, is, metadata);    
            //解析結果  
            resultStr = putResult.getETag();  
        } catch (Exception e) {  
            e.printStackTrace();  
            LOGGER.error("上傳阿里雲OSS服務器異常." + e.getMessage(), e);    
        }  
        return resultStr;  
    }  
    
    /** 
     * 通過文件名判斷並獲取OSS服務文件上傳時文件的contentType 
     * @param fileName 文件名 
     * @return 文件的contentType 
     */  
     static  String getContentType(String fileName) {  
        //文件的後綴名  
        String fileExtension = fileName.substring(fileName.lastIndexOf(".")); 
        fileExtension = fileExtension.trim();
        if (".bmp".equalsIgnoreCase(fileExtension)) {  
            return "image/bmp";  
        }  
        if (".gif".equalsIgnoreCase(fileExtension)) {  
            return "image/gif";  
        }  
        if (".jpeg".equalsIgnoreCase(fileExtension) || ".jpg".equalsIgnoreCase(fileExtension)  || ".png".equalsIgnoreCase(fileExtension)) {  
            return "image/jpeg";  
        }  
        if (".html".equalsIgnoreCase(fileExtension)) {  
            return "text/html";  
        }  
        if (".txt".equalsIgnoreCase(fileExtension)) {  
            return "text/plain";  
        }  
        if (".vsd".equalsIgnoreCase(fileExtension)) {  
            return "application/vnd.visio";  
        }  
        if (".ppt".equalsIgnoreCase(fileExtension) || ".pptx".equalsIgnoreCase(fileExtension)) {  
            return "application/vnd.ms-powerpoint";  
        }  
        if (".doc".equalsIgnoreCase(fileExtension) || ".docx".equalsIgnoreCase(fileExtension)) {  
            return "application/msword";  
        }  
        if (".xml".equalsIgnoreCase(fileExtension)) {  
            return "text/xml";  
        }  
        if (".apk".equalsIgnoreCase(fileExtension)) {  
            return "application/vnd.android.package-archive";  
        }  
        if (".mp4".equalsIgnoreCase(fileExtension)) {
        	return "video/mp4";
        }
        if (".x-flv".equalsIgnoreCase(fileExtension)) {
        	return "video/x-flv";
        }
        if (".avi".equalsIgnoreCase(fileExtension)) {
        	return "video/avi";
        }
        if (".rmvb".equalsIgnoreCase(fileExtension) || ".rm".equalsIgnoreCase(fileExtension)) {
        	return "application/octet-stream";
        }
        if (".wmv".equalsIgnoreCase(fileExtension)) {
        	return "video/x-ms-wmv";
        }
        if (".mov".equalsIgnoreCase(fileExtension)) {
        	return "video/quicktime";
        }
        if (".3gp".equalsIgnoreCase(fileExtension)) {
        	return "video/3gpp";
        }
        //默認返回類型  
        return "image/jpeg";  
    }  

    /**
     * 獲得url鏈接 
     * @param key key
     * @param ossClient ossClient
     * @param backetName backetName
     * @return String
     */
     static String getUrl(String key, OSSClient ossClient, String backetName) {  
        // 設置URL有效期 
        Date expiration = new Date(System.currentTimeMillis() + 3600L * 1000 * 24 * 365 * 100);  
        // 生成URL  
        URL url = ossClient.generatePresignedUrl(backetName, key, expiration);  
        if (url != null) {  
            return url.toString();  
        }  
        return null;  
    }  
    
    /**
     * 獲得返回路徑  
     * @param fileUrl fileUrl
     * @param ossClient ossClient
     * @param backetName backetName
     * @param folder folder
     * @return String
     */
    static String getImgUrl(String fileUrl, OSSClient ossClient, String backetName, String folder) {  
        LOGGER.info(fileUrl);  
        if (!StringUtils.isEmpty(fileUrl)) {
            String[] split = fileUrl.split("/");  
            return getUrl(folder + split[split.length - 1], ossClient, backetName);  
        }  
        return null;  
    }  

   /**
	* 文件上傳
	* 返回oss網絡路徑,可直接瀏覽器訪問或下載
	* @param folder folder
	* @param file file
	* @return String
    */
   public static String aLiYunOssUpload(File file, String folder) {
  	  //初始化OSSClient  
      OSSClient ossClient = AliyunOSSUtil.getOSSClient();  
      String md5key = AliyunOSSUtil.uploadObject2OSS(ossClient, file, ossEntity.getBacketName(), folder);
      LOGGER.info("上傳後的文件MD5數字唯一簽名:" + md5key);  
      //String imgUrl = getImgUrl(name, ossClient, BACKET_NAME, folder);
      String imgUrl = "https://" + ossEntity.getBacketName() + "." + ossEntity.getEndPoint() + "/" + folder + file.getName();
      //返回網絡路徑
      return imgUrl;
   }
   
    /**
     *  文件刪除(批量)
     * @param keys keys
     * @return Map
     */
	public static Map<String, Object> aLiYunOssDelete(List<String> keys) {
		Map<String, Object> result = new HashMap<String, Object>();
		//初始化OSSClient  
	    OSSClient ossClient = AliyunOSSUtil.getOSSClient();  
		try {
			DeleteObjectsResult deleteObjectsResult = ossClient.deleteObjects(new DeleteObjectsRequest(ossEntity.getBacketName()).withKeys(keys));
			//返回的成功刪除Object的結果
			List<String> deletedObjects = deleteObjectsResult.getDeletedObjects();
			int count = 0;
			for (String string : deletedObjects) {
				count = count + 1;
				result.put("item" + count, "第" + count + "張:" + string);
			}
			if (keys.size() == deletedObjects.size()) {
				result.put("status", 0);
			} else {
				result.put("status", 1);
			}
			result.put("successCount", deletedObjects.size());
			// 關閉client
			ossClient.shutdown();
		} catch (Exception e) {
			LOGGER.error("刪除--阿里雲OSS服務器異常." + e.getMessage(), e);    
			result.put("status", 1);
			result.put("msg", "刪除--阿里雲OSS服務器異常.");
		}
	    //返回網絡路徑
	    return result;
	}
}

  • 5.4
    AliyunOSSUtil 和 OSSClientConstants創建完之後,再來添加核心 : 自動配置類
    我這裏取名 : AliyunAutoConfiguration :
    作用是,將你的寫好的工具類註冊成組件
    PS :
    @EnableConfigurationProperties 用於綁定屬性文件,也就是OSSClientConstants類
package com.expansion.starter.config;

import com.expansion.starter.aliyunoss.AliyunOSSUtil;
import com.expansion.starter.aliyunoss.OSSClientConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Auther: lj
 * 阿里雲自動配置類
 * @EnableConfigurationProperties    綁定屬性文件
 */
@Configuration
@EnableConfigurationProperties(OSSClientConstants.class)
public class AliyunAutoConfiguration {

    @Autowired
    OSSClientConstants ossEntity;

    /**
     * oss組件
     * @return AliyunOSSUtil
     */
    @Bean
    public AliyunOSSUtil aliyunOSSUtil(){
        AliyunOSSUtil aliyunOSSUtil = new AliyunOSSUtil();
        aliyunOSSUtil.setOssEntity(ossEntity);
        return aliyunOSSUtil;
    }
}

  • 5.5
    到這一步基本完成,現在我們就可以打包測試了
    5.5.1
    用mvn命令或者點擊最右側的 Maven projects
    首先選擇 (自動配置模塊) 進行 install
    然後選擇 (啓動器模塊) 進行 install
    在這裏插入圖片描述
    到這一步,starter製作基本完成
    接下來就可以測試了

6 測試

  • 6.1用Spring Initializr快速 創建一個新的springboot工程

  • 6.2 添加自定義的starter依賴:

 <!--引入自定義的starter-->
        <dependency>
            <groupId>com.expansion</groupId>
            <artifactId>expansion-spring-boot-starter</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
  • 6.3
    在application.properties或者.yml裏面設置 阿里雲oss所需的屬性:
    這裏就是在 5.2 步驟的時候用 @ConfigurationProperties指定的prefix

application.properties:


expansion.aliyun-oss.endPoint = XXX
expansion.aliyun-oss.accessKeyId = XXX
expansion.aliyun-oss.accessKeySecret = XXX
expansion.aliyun-oss.backetName = XXX

  • 6.4
    編寫controller進行測試
/**
 * @Auther: lj
 */
@Controller
public class TestController {

    @Autowired
    AliyunOSSUtil aliyunOSSUtil;

    @ResponseBody
    @GetMapping("/upload")
    public String upload() {
        //將E盤下的文件上傳給oss並返回oss路徑
        String files="E:\\pic\\11.jpg";
        File file =new File(files);
        return aliyunOSSUtil.aLiYunOssUpload(file, "test文件夾");
    }
}

7. 到這裏基本就完成了對oss 自定義starter的編寫, 可以依照着這個模式,還可以自定義一些工具類,下面是會用到的一些註解介紹

 @Configuration  //指定這個類是一個配置類
@ConditionalOnXXX  //在指定條件成立的情況下自動配置類生效
@AutoConfigureAfter  //指定自動配置類的順序
@Bean  //給容器中添加組件
​
@ConfigurationPropertie//結合相關xxxProperties類來綁定相關的配置
@EnableConfigurationProperties //讓xxxProperties生效加入到容器中
​
//自動配置類要能加載
// 將需要啓動就加載的自動配置類,配置在META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\

下面是你根據自己條件 ,可能會用到的一些條件註解介紹 :

@ConditionalOnClass:當類路徑classpath下有指定的類的情況下進行自動配置
@ConditionalOnMissingBean:當容器(Spring Context)中沒有指定Bean的情況下進行自動配置
@ConditionalOnProperty(prefix = “example.service”, value = “enabled”, matchIfMissing = true),當配置文件中example.service.enabled=true時進行自動配置,如果沒有設置此值就默認使用matchIfMissing對應的值
@ConditionalOnMissingBean,當Spring Context中不存在該Bean時。
@ConditionalOnBean:當容器(Spring Context)中有指定的Bean的條件下
@ConditionalOnMissingClass:當類路徑下沒有指定的類的條件下
@ConditionalOnExpression:基於SpEL表達式作爲判斷條件
@ConditionalOnJava:基於JVM版本作爲判斷條件
@ConditionalOnJndi:在JNDI存在的條件下查找指定的位置
@ConditionalOnNotWebApplication:當前項目不是Web項目的條件下
@ConditionalOnWebApplication:當前項目是Web項目的條件下
@ConditionalOnResource:類路徑下是否有指定的資源
@ConditionalOnSingleCandidate:當指定的Bean在容器中只有一個,或者在有多個Bean的情況下,用來指定首選的Bean

8. 自定義starter模式步驟

1 . 創建一個啓動器,啓動器只用來做依賴導入;
2 . 專門來寫一個自動配置模塊;
3 . 啓動器依賴自動配置;別人只需要引入啓動器(starter)

9.大家只需要照着這個模式,寫一個組件(啓動器(starter)), 別人就只需要引入, 減少了大量繁瑣的配置和代碼的編寫, 大大的提高了重用性也簡化了開發步驟

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章