SpringBoot集成MaxCompute的示例代碼
1、SDK方式集成
使用odps-sdk-core集成, 官方文檔地址MaxCompute Java SDK介紹
1.1、依賴(lài)引入odps-sdk-core
<properties> <java.version>1.8</java.version> <!--maxCompute sdk 版本號(hào)--> <max-compute-sdk.version>0.40.8-public</max-compute-sdk.version> </properties> <dependencies> <!--max compute sdk--> <dependency> <groupId>com.aliyun.odps</groupId> <artifactId>odps-sdk-core</artifactId> <version>${max-compute-sdk.version}</version> </dependency> </dependencies>
1.2、編寫(xiě)連接工具類(lèi)
編寫(xiě)MaxComputeSdkUtil以SDK方式連接MaxCompute
1.2.1、重要類(lèi)和方法說(shuō)明
1、連接參數(shù)類(lèi):
@Data public class MaxComputeSdkConnParam { /**阿里云accessId 相當(dāng)于用戶(hù)名 */ private String aliyunAccessId; /**阿里云accessKey 相當(dāng)于密碼 */ private String aliyunAccessKey; /**阿里云maxCompute服務(wù)接口地址 默認(rèn)是http://service.odps.aliyun.com/api*/ private String maxComputeEndpoint; /**項(xiàng)目名稱(chēng)*/ private String projectName; }
2、查詢(xún)表元數(shù)據(jù)信息實(shí)體
主要是字段:tableName, comment。還可以自己添加其他字段
@Data @NoArgsConstructor @AllArgsConstructor public class TableMetaInfo { /**表名稱(chēng)*/ private String tableName; /**表注釋*/ private String comment; }
3、公共方法(初始化)
/**默認(rèn)的odps接口地址 在Odps中也可以看到該變量*/ private static final String defaultEndpoint = "http://service.odps.aliyun.com/api"; /**開(kāi)啟全表掃描的配置*/ private static final String FULL_SCAN_CONFIG = "odps.sql.allow.fullscan"; /**分頁(yè)查詢(xún)sql模板*/ private static final String PAGE_SELECT_TEMPLATE_SQL = "select z.* from (%s) z limit %s, %s;"; /**分頁(yè)查詢(xún)統(tǒng)計(jì)數(shù)量模板SQL*/ private static final String PAGE_COUNT_TEMPLATE_SQL = "select count(1) from (%s) z;"; /**sdk的odps客戶(hù)端*/ private final Odps odps; /**odps連接參數(shù)*/ private final MaxComputeSdkConnParam connParam; public MaxComputeSdkUtil(MaxComputeSdkConnParam param){ this.connParam = param; // 構(gòu)建odps客戶(hù)端 this.odps = buildOdps(); } /** * 構(gòu)建odps客戶(hù)端 用于執(zhí)行sql等操作 * @return odps客戶(hù)端 */ private Odps buildOdps() { // 阿里云賬號(hào)密碼 AccessId 和 AccessKey final String aliyunAccessId = connParam.getAliyunAccessId(); final String aliyunAccessKey = connParam.getAliyunAccessKey(); // 創(chuàng)建阿里云賬戶(hù) final AliyunAccount aliyunAccount = new AliyunAccount(aliyunAccessId, aliyunAccessKey); // 使用阿里云賬戶(hù)創(chuàng)建odps客戶(hù)端 final Odps odps = new Odps(aliyunAccount); // 傳入了的話(huà)就是用傳入的 沒(méi)有傳入使用默認(rèn)的 final String endpoint = connParam.getMaxComputeEndpoint(); try { odps.setEndpoint(ObjectUtils.isEmpty(endpoint) ? defaultEndpoint : endpoint); } catch (Exception e) { // 端點(diǎn)格式不正確 throw new BizException(ResultCode.MAX_COMPUTE_ENDPOINT_ERR); } // 設(shè)置項(xiàng)目 odps.setDefaultProject(connParam.getProjectName()); return odps; }
4、查詢(xún)表信息
/** * 獲取表信息 */ public List<TableMetaInfo> getTableInfos(){ final Tables tables = odps.tables(); List<TableMetaInfo> resultTables = new ArrayList<>(); try { for (Table table : tables) { // tableName final String name = table.getName(); // 描述 final String comment = table.getComment(); final TableMetaInfo info = new TableMetaInfo(name, comment); resultTables.add(info); } } catch (Exception e) { e.printStackTrace(); final String errMsg = ObjectUtils.isEmpty(e.getMessage()) ? "" : e.getMessage(); if (errMsg.contains("ODPS-0410051:Invalid credentials")){ throw new BizException(ResultCode.MAX_COMPUTE_UNAME_ERR); } if (errMsg.contains("ODPS-0410042:Invalid signature value")){ throw new BizException(ResultCode.MAX_COMPUTE_PWD_ERR); } if (errMsg.contains("ODPS-0420095: Access Denied")){ throw new BizException(ResultCode.MAX_COMPUTE_PROJECT_ERR); } } return resultTables; }
5、執(zhí)行SQL封裝
/** * 執(zhí)行sql查詢(xún) * @param querySql 查詢(xún)sql * @param fullScan 是否開(kāi)啟全表掃描 如果查詢(xún)多個(gè)分區(qū)數(shù)據(jù),需要開(kāi)啟全表掃描 * @return List<Map<String, Object>> */ public List<Map<String, Object>> queryData(String querySql, boolean fullScan){ try { // 配置全表掃描嗎 configFullScan(fullScan); // 使用任務(wù)執(zhí)行SQL final Instance instance = SQLTask.run(odps, querySql); // 等待執(zhí)行成功 instance.waitForSuccess(); // 封裝返回結(jié)果 List<Record> records = SQLTask.getResult(instance); // 結(jié)果轉(zhuǎn)換為Map return buildMapByRecords(records); } catch (OdpsException e) { e.printStackTrace(); throw new BizException(ResultCode.MAX_COMPUTE_SQL_EXEC_ERR); } } /** * 開(kāi)啟和移除全表掃描配置 * @param fullScan 是否全表掃描 */ private void configFullScan(boolean fullScan) { if (fullScan){ // 開(kāi)啟全表掃描配置 Map<String, String> config = new HashMap<>(); log.info("===>>開(kāi)啟全表掃描, 查詢(xún)多個(gè)分區(qū)數(shù)據(jù)"); config.put(FULL_SCAN_CONFIG, "true"); odps.setGlobalSettings(config); }else { // 移除全表掃描配置 odps.getGlobalSettings().remove(FULL_SCAN_CONFIG); } } /** * 將List<Record>準(zhǔn)換為L(zhǎng)ist<Map></> * @param records sql查詢(xún)結(jié)果 * @return 返回結(jié)果 */ private List<Map<String, Object>> buildMapByRecords(List<Record> records) { List<Map<String, Object>> listMap = new ArrayList<>(); for (Record record : records) { Column[] columns = record.getColumns(); Map<String, Object> map = new LinkedHashMap<>(); for (Column column : columns) { String name = column.getName(); Object value = record.get(name); // maxCompute里面的空返回的是使用\n if ("\\N".equalsIgnoreCase(String.valueOf(value))) { map.put(name, ""); } else { map.put(name, value); } } listMap.add(map); } return listMap; }
6、分頁(yè)查詢(xún)分裝
/** * 執(zhí)行sql查詢(xún)【分頁(yè)查詢(xún)】 * @param querySql 查詢(xún)sql * @param page 頁(yè)碼 從1開(kāi)始 第n頁(yè)傳n * @param size 每頁(yè)記錄數(shù) * @param fullScan 是否開(kāi)啟全表掃描 如果查詢(xún)多個(gè)分區(qū)數(shù)據(jù),需要開(kāi)啟全表掃描 * @return List<Map<String, Object>> */ public List<Map<String, Object>> queryData(String querySql, Integer page, Integer size, boolean fullScan){ // 重寫(xiě)SQl,添加limit offset, limit // 1、替換分號(hào) querySql = querySql.replaceAll(";", ""); // 2、格式化SQL Integer offset = (page - 1 ) * size; // 得到執(zhí)行sql final String execSql = String.format(PAGE_SELECT_TEMPLATE_SQL, querySql, offset, size); log.info("=======>>>執(zhí)行分頁(yè)sql為:{}", execSql); // 調(diào)用執(zhí)行SQL數(shù)據(jù) return queryData(execSql, fullScan); } /** * 執(zhí)行分頁(yè)查詢(xún) * @param querySql 分頁(yè)查詢(xún)sql * @param page 頁(yè)碼 從1開(kāi)始 第n頁(yè)傳n * @param size 每頁(yè)記錄數(shù) * @return 分頁(yè)查詢(xún)結(jié)果 */ public PageResult<Map<String, Object>> pageQueryMap(String querySql, Integer page, Integer size){ // 1、替換分號(hào) querySql = querySql.replaceAll(";", ""); String countSql = String.format(PAGE_COUNT_TEMPLATE_SQL, querySql); log.info("=======>>>執(zhí)行分頁(yè)統(tǒng)計(jì)總數(shù)sql為:{}", countSql); // 查詢(xún)總數(shù) final List<Map<String, Object>> countMap = queryData(countSql, false); if (CollectionUtils.isEmpty(countMap)){ return new PageResult<>(0L, new ArrayList<>()); } long count = 0L; for (Object value : countMap.get(0).values()) { count = Long.parseLong(String.valueOf(value)); } if (count == 0){ return new PageResult<>(0L, new ArrayList<>()); } // 執(zhí)行分頁(yè)查詢(xún) 開(kāi)啟全表掃描 final List<Map<String, Object>> resultList = queryData(querySql, page, size, true); return new PageResult<>(count, resultList); } /** * 執(zhí)行分頁(yè)查詢(xún) * @param querySql 分頁(yè)查詢(xún)sql * @param page 頁(yè)碼 從1開(kāi)始 第n頁(yè)傳n * @param size 每頁(yè)記錄數(shù) * @return 分頁(yè)查詢(xún)結(jié)果 */ public <T>PageResult<T> pageQuery(String querySql, Integer page, Integer size, Class<T> clazz){ final PageResult<Map<String, Object>> result = pageQueryMap(querySql, page, size); List<T> rows = new ArrayList<>(); for (Map<String, Object> row : result.getRows()) { final T t = JSONObject.parseObject(JSONObject.toJSONString(row), clazz); rows.add(t); } return new PageResult<>(result.getTotal(), rows); }
1.2.2 工具類(lèi)測(cè)試
使用測(cè)試數(shù)據(jù)測(cè)試工具類(lèi)
public static void main(String[] args) { // 構(gòu)建連接參數(shù) final MaxComputeSdkConnParam connParam = new MaxComputeSdkConnParam(); connParam.setAliyunAccessId("您的阿里云賬號(hào)accessId"); connParam.setAliyunAccessKey("您的阿里云賬號(hào)accessKey"); connParam.setProjectName("項(xiàng)目名"); // 實(shí)例化工具類(lèi) final MaxComputeSdkUtil sdkUtil = new MaxComputeSdkUtil(connParam); // 查詢(xún)所有表 final List<TableMetaInfo> tableInfos = sdkUtil.getTableInfos(); for (TableMetaInfo tableInfo : tableInfos) { System.out.println(tableInfo.getTableName()); } // 分頁(yè)查詢(xún)數(shù)據(jù) final PageResult<Map<String, Object>> page = sdkUtil.pageQueryMap("select * from ods_cust;", 2, 10); System.out.println(page.getTotal()); for (Map<String, Object> map : page.getRows()) { System.out.println(JSONObject.toJSONString(map)); } }
1.2.3 為什么要開(kāi)啟全表掃描
maxCompute存在使用限制如下:
當(dāng)使用select語(yǔ)句時(shí),屏顯最多只能顯示10000行結(jié)果。當(dāng)select語(yǔ)句作為子句時(shí)則無(wú)此限制,select子句會(huì)將全部結(jié)果返回給上層查詢(xún)。
select語(yǔ)句查詢(xún)分區(qū)表時(shí)默認(rèn)禁止全表掃描。
自2018年1月10日20:00:00后,在新創(chuàng)建的項(xiàng)目上執(zhí)行SQL語(yǔ)句時(shí),默認(rèn)情況下,針對(duì)該項(xiàng)目里的分區(qū)表不允許執(zhí)行全表掃描操作。在查詢(xún)分區(qū)表數(shù)據(jù)時(shí)必須指定分區(qū),由此減少SQL的不必要I/O,從而減少計(jì)算資源的浪費(fèi)以及按量計(jì)費(fèi)模式下不必要的計(jì)算費(fèi)用。
如果您需要對(duì)分區(qū)表進(jìn)行全表掃描,可以在全表掃描的SQL語(yǔ)句前加上命令set odps.sql.allow.fullscan=true;,并和SQL語(yǔ)句一起提交執(zhí)行。假設(shè)sale_detail表為分區(qū)表,需要同時(shí)執(zhí)行如下語(yǔ)句進(jìn)行全表查詢(xún):
2、JDBC方式集成
使用odps-jdbc集成, 官方文檔地址MaxCompute Java JDBC介紹
<properties> <java.version>1.8</java.version> <!--maxCompute-jdbc-版本號(hào)--> <max-compute-jdbc.version>3.0.1</max-compute-jdbc.version> </properties> <dependencies> <!--max compute jdbc--> <dependency> <groupId>com.aliyun.odps</groupId> <artifactId>odps-jdbc</artifactId> <version>${max-compute-jdbc.version}</version> <classifier>jar-with-dependencies</classifier> </dependency> </dependencies>
2.2、編寫(xiě)連接工具類(lèi)
編寫(xiě)MaxComputeSdkUtil以JDBC方式連接MaxCompute
2.2.1、重要類(lèi)和方法說(shuō)明
1、連接參數(shù)類(lèi):
@Data public class MaxComputeJdbcConnParam { /**阿里云accessId 相當(dāng)于用戶(hù)名 */ private String aliyunAccessId; /**阿里云accessKey 相當(dāng)于密碼 */ private String aliyunAccessKey; /** maxcompute_endpoint */ private String endpoint; /**項(xiàng)目名稱(chēng)*/ private String projectName; }
2、公共方法(初始化)
/**JDBC 驅(qū)動(dòng)名稱(chēng)*/ private static final String DRIVER_NAME = "com.aliyun.odps.jdbc.OdpsDriver"; private static final String SELECT_ALL_TABLE_SQL = "select table_name, table_comment from Information_Schema.TABLES"; private static final String SELECT_FIELD_BY_TABLE_SQL = "select column_name, column_comment from Information_Schema.COLUMNS where table_name = '%s'"; /**分頁(yè)查詢(xún)sql模板*/ private static final String PAGE_SELECT_TEMPLATE_SQL = "select z.* from (%s) z limit %s, %s;"; /**分頁(yè)查詢(xún)統(tǒng)計(jì)數(shù)量模板SQL*/ private static final String PAGE_COUNT_TEMPLATE_SQL = "select count(1) from (%s) z;"; /**連接*/ private final Connection conn; /** * 連接參數(shù) */ private final MaxComputeJdbcConnParam connParam; public MaxComputeJdbcUtil(MaxComputeJdbcConnParam connParam) { this.connParam = connParam; this.conn = buildConn(); } /** * 創(chuàng)建連接 * @return 數(shù)據(jù)庫(kù)連接 */ private Connection buildConn() { try { Class.forName(DRIVER_NAME); } catch (ClassNotFoundException e) { e.printStackTrace(); throw new BizException(ResultCode.MAX_COMPUTE_JDBC_DRIVE_LOAD_ERR); } try { // JDBCURL連接模板 String jdbcUrlTemplate = "jdbc:odps:%s?project=%s&useProjectTimeZone=true"; // 使用驅(qū)動(dòng)管理器連接獲取連接 return DriverManager.getConnection( String.format(jdbcUrlTemplate, connParam.getEndpoint(), connParam.getProjectName()), connParam.getAliyunAccessId(), connParam.getAliyunAccessKey()); } catch (SQLException e) { e.printStackTrace(); throw new BizException(ResultCode.MAX_COMPUTE_JDBC_DRIVE_LOAD_ERR); } }
3、查詢(xún)表信息
/** * 獲取表信息 * @return 表信息列表 */ public List<TableMetaInfo> getTableInfos(){ List<TableMetaInfo> resultList = new ArrayList<>(); Statement statement = null; ResultSet resultSet = null; try { // 創(chuàng)建statement 使用SQL直接查詢(xún) statement = conn.createStatement(); // 執(zhí)行查詢(xún)語(yǔ)句 resultSet = statement.executeQuery(SELECT_ALL_TABLE_SQL); while (resultSet.next()){ final String tableName = resultSet.getString("table_name"); final String tableComment = resultSet.getString("table_comment"); final TableMetaInfo info = new TableMetaInfo(tableName, tableComment); resultList.add(info); } return resultList; } catch (SQLException e) { e.printStackTrace(); throw new BizException(ResultCode.MAX_COMPUTE_SQL_EXEC_ERR); } finally { // 關(guān)閉resultSet closeResultSet(resultSet); // 關(guān)閉statement closeStatement(statement); } }
4、執(zhí)行SQL封裝
/** * 執(zhí)行sql查詢(xún) * @param querySql 查詢(xún)sql * @return List<Map<String, Object>> */ public List<Map<String, Object>> queryData(String querySql){ List<Map<String, Object>> resultList = new ArrayList<>(); Statement statement = null; ResultSet resultSet = null; try { // 創(chuàng)建statement statement = conn.createStatement(); // 執(zhí)行查詢(xún)語(yǔ)句 resultSet = statement.executeQuery(querySql); // 構(gòu)建結(jié)果返回 buildMapByRs(resultList, resultSet); return resultList; } catch (SQLException e) { e.printStackTrace(); throw new BizException(ResultCode.MAX_COMPUTE_SQL_EXEC_ERR); } finally { // 關(guān)閉resultSet closeResultSet(resultSet); // 關(guān)閉statement closeStatement(statement); } } /** * 將ResultSet轉(zhuǎn)換為L(zhǎng)ist<Map<String, Object>> * @param resultList 轉(zhuǎn)換的集合 * @param resultSet ResultSet * @throws SQLException e */ private void buildMapByRs(List<Map<String, Object>> resultList, ResultSet resultSet) throws SQLException { // 獲取元數(shù)據(jù) ResultSetMetaData metaData = resultSet.getMetaData(); while (resultSet.next()) { // 獲取列數(shù) int columnCount = metaData.getColumnCount(); Map<String, Object> map = new HashMap<>(); for (int i = 0; i < columnCount; i++) { String columnName = metaData.getColumnName(i + 1); Object object = resultSet.getObject(columnName); // maxCompute里面的空返回的是使用\n if ("\\N".equalsIgnoreCase(String.valueOf(object))) { map.put(columnName, ""); } else { map.put(columnName, object); } } resultList.add(map); } } private void closeStatement(Statement statement){ if (statement != null){ try { statement.close(); } catch (SQLException e) { e.printStackTrace(); } } } private void closeResultSet(ResultSet resultSet){ if (resultSet != null){ try { resultSet.close(); } catch (SQLException e) { e.printStackTrace(); } } }
5、分頁(yè)查詢(xún)分裝
/** * 執(zhí)行sql查詢(xún) * @param querySql 查詢(xún)sql * @return List<Map<String, Object>> */ public List<Map<String, Object>> queryData(String querySql, Integer page, Integer size){ List<Map<String, Object>> resultList = new ArrayList<>(); Statement statement = null; ResultSet resultSet = null; try { // 1、替換分號(hào) querySql = querySql.replaceAll(";", ""); // 創(chuàng)建statement statement = conn.createStatement(); // 2、格式化SQL int offset = (page - 1 ) * size; final String execSql = String.format(PAGE_SELECT_TEMPLATE_SQL, querySql, offset, size); log.info("=======>>>執(zhí)行分頁(yè)sql為:{}", execSql); // 執(zhí)行查詢(xún)語(yǔ)句 resultSet = statement.executeQuery(execSql); // 構(gòu)建結(jié)果返回 buildMapByRs(resultList, resultSet); return resultList; } catch (SQLException e) { e.printStackTrace(); throw new BizException(ResultCode.MAX_COMPUTE_SQL_EXEC_ERR); } finally { // 關(guān)閉resultSet closeResultSet(resultSet); // 關(guān)閉statement closeStatement(statement); } } /** * 執(zhí)行分頁(yè)查詢(xún) * @param querySql 分頁(yè)查詢(xún)sql * @param page 頁(yè)碼 從1開(kāi)始 第n頁(yè)傳n * @param size 每頁(yè)記錄數(shù) * @return 分頁(yè)查詢(xún)結(jié)果 */ public PageResult<Map<String, Object>> pageQueryMap(String querySql, Integer page, Integer size){ // 1、替換分號(hào) querySql = querySql.replaceAll(";", ""); String countSql = String.format(PAGE_COUNT_TEMPLATE_SQL, querySql); log.info("=======>>>執(zhí)行分頁(yè)統(tǒng)計(jì)總數(shù)sql為:{}", countSql); // 查詢(xún)總數(shù) final List<Map<String, Object>> countMap = queryData(countSql); if (CollectionUtils.isEmpty(countMap)){ return new PageResult<>(0L, new ArrayList<>()); } long count = 0L; for (Object value : countMap.get(0).values()) { count = Long.parseLong(String.valueOf(value)); } if (count == 0){ return new PageResult<>(0L, new ArrayList<>()); } // 執(zhí)行分頁(yè)查詢(xún) 開(kāi)啟全表掃描 final List<Map<String, Object>> resultList = queryData(querySql, page, size); return new PageResult<>(count, resultList); } /** * 執(zhí)行分頁(yè)查詢(xún) * @param querySql 分頁(yè)查詢(xún)sql * @param page 頁(yè)碼 從1開(kāi)始 第n頁(yè)傳n * @param size 每頁(yè)記錄數(shù) * @return 分頁(yè)查詢(xún)結(jié)果 */ public <T>PageResult<T> pageQuery(String querySql, Integer page, Integer size, Class<T> clazz){ final PageResult<Map<String, Object>> result = pageQueryMap(querySql, page, size); List<T> rows = new ArrayList<>(); for (Map<String, Object> row : result.getRows()) { final T t = JSONObject.parseObject(JSONObject.toJSONString(row), clazz); rows.add(t); } return new PageResult<>(result.getTotal(), rows); }
2.2.2 工具類(lèi)測(cè)試
使用測(cè)試數(shù)據(jù)測(cè)試工具類(lèi)
public static void main(String[] args) { final MaxComputeJdbcConnParam connParam = new MaxComputeJdbcConnParam(); connParam.setAliyunAccessId("您的阿里云賬號(hào)accessId"); connParam.setAliyunAccessKey("您的阿里云賬號(hào)accessKey"); connParam.setProjectName("項(xiàng)目名"); connParam.setEndpoint("http://service.cn-hangzhou.maxcompute.aliyun.com/api"); final MaxComputeJdbcUtil jdbcUtil = new MaxComputeJdbcUtil(connParam); // 獲取表信息 final List<TableMetaInfo> tableInfos = jdbcUtil.getTableInfos(); for (TableMetaInfo tableInfo : tableInfos) { System.out.println(tableInfo); } // 獲取字段信息 final String tableName = tableInfos.get(new Random().nextInt(tableInfos.size())).getTableName(); final List<TableColumnMetaInfo> fields = jdbcUtil.getFieldByTableName(tableName); for (TableColumnMetaInfo field : fields) { System.out.println(field.getFieldName() + "-" + field.getComment()); } // 執(zhí)行查詢(xún) final List<Map<String, Object>> list = jdbcUtil.queryData("select * from ods_cust;"); for (Map<String, Object> map : list) { System.out.println(JSONObject.toJSONString(map)); } // 執(zhí)行分頁(yè)查詢(xún) final List<Map<String, Object>> list2 = jdbcUtil.queryData("select * from ods_cust;", 2, 10); for (Map<String, Object> map : list2) { System.out.println(JSONObject.toJSONString(map)); } // 執(zhí)行分頁(yè)查詢(xún) 并返回count final PageResult<Map<String, Object>> list3 = jdbcUtil.pageQueryMap("select * from ods_cust;", 2, 10); System.out.println(list3.getTotal()); for (Map<String, Object> map : list3.getRows()) { System.out.println(JSONObject.toJSONString(map)); } jdbcUtil.close(); }
項(xiàng)目地址
到此這篇關(guān)于SpringBoot集成MaxCompute的文章就介紹到這了,更多相關(guān)SpringBoot集成MaxCompute內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解Java編程中包package的內(nèi)容與包對(duì)象的規(guī)范
這篇文章主要介紹了Java編程中包package的內(nèi)容與包對(duì)象的規(guī)范,是Java入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-12-12Java的Synchronized關(guān)鍵字學(xué)習(xí)指南(全面 & 詳細(xì))
這篇文章主要給大家介紹了關(guān)于Java的Synchronized關(guān)鍵字的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03spring boot項(xiàng)目application.properties文件存放及使用介紹
這篇文章主要介紹了spring boot項(xiàng)目application.properties文件存放及使用介紹,我們的application.properties文件中會(huì)有很多敏感信息,大家在使用過(guò)程中要多加小心2021-06-06Java設(shè)計(jì)模式詳解之門(mén)面模式(外觀(guān)模式)
為子系統(tǒng)中的一組接口提供一個(gè)一致的界面, Facade 模式定義了一個(gè)高層接口,這個(gè)接口使得這一子系統(tǒng)更加容易使用。本文給大家介紹Java設(shè)計(jì)模式詳解之門(mén)面模式(外觀(guān)模式),感興趣的朋友參考下吧2016-04-04SpringBoot實(shí)現(xiàn)發(fā)送郵件功能過(guò)程圖解
這篇文章主要介紹了SpringBoot實(shí)現(xiàn)發(fā)送郵件功能過(guò)程圖解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03Java的DataInputStream和DataOutputStream數(shù)據(jù)輸入輸出流
這里我們來(lái)看一下Java的DataInputStream和DataOutputStream數(shù)據(jù)輸入輸出流的使用示例,兩個(gè)類(lèi)分別繼承于FilterInputStream和FilterOutputStream:2016-06-06