SpringBoot | 第三十四章:CXF構建WebService服務
前言
上一章節,講解了如何使用
Spring-WS
構建WebService
服務。其實,建立WebService
的方式有很多的,今天來看看如何使用apache cxf
來構建及呼叫WebService
服務。
一點知識
何為Apache-CXF
Apache CXF
是一個開源的Services
框架,CXF
幫助您利用Frontend
程式設計 API 來構建和開發Services,像JAX-WS
、JAX-RS
。這些Services
可以支援多種協議,比如:SOAP
、XML/HTTP
、RESTful HTTP
或者CORBA
,並且可以在多種傳輸協議上執行,比如:HTTP
、JMS
或者JBI
,CXF大大簡化了 Services 的建立,同時它可以天然地和Spring進行無縫整合。
最常用的是使用cxf
開發web-service
。本身是基於JAX-WS
規範來實現的。當然,本身CXF
也實現了JAX-RS
規範來實現RESTFul Service
。
關於JAX-WS規範
JAX-WS
全稱:Java API for XML-Based Web Services
。JAX-WS
是一種程式設計模型,它通過支援將基於註釋的標準模型用於開發Web Service
應用程式和客戶機來簡化應用程式開發。
JAX-WS
是Java程式設計語言一個用來建立Web服務的API。
- 在伺服器端,使用者只需要通過
Java
語言定義遠端呼叫所需要實現的介面SEI(service endpoint interface)
JAX-WS
的服務釋出介面就可以將其釋出為WebService
介面。 - 在客戶端,使用者可以通過
JAX-WS
的API建立一個代理(用本地物件來替代遠端的服務)來實現對於遠端伺服器端的呼叫。當然JAX-WS
也提供了一組針對底層訊息進行操作的API呼叫,你可以通過Dispatch
直接使用SOAP訊息或XML訊息傳送請求或者使用Provider處理SOAP或XML訊息。
常用註解介紹
JAX-WS
提供了一系列的註解,可以對WebService
的介面規範化。以下介紹下最常用的幾個註解。
-
@WebService:用於將Java類標記為實現
Web Service
或者將服務端點介面 (SEI) 標記為實現Web Service
- name:此屬性的值包含XML Web Service的名稱。在預設情況下,該值是實現XML Web Service的類的名稱,wsdl:portType 的名稱。預設值為 Java 類的簡單名稱 + Service。(字串)
- targetNamespace:預設的值為 “http://包名/” ,可以通過此變數指定一個自定義的targetNamespace值。
- serviceName:對外發布的服務名,指定
Web Service
的服務名稱:wsdl:service。預設值為 Java 類的簡單名稱 + Service。(字串) - endpointInterface:
- portName:wsdl:portName的值。預設值為
WebService.name+Port
- wsdlLocation:指定用於定義 Web Service 的 WSDL 文件的 Web 地址
-
@WebMethod:表示作為一項
Web Service
操作的方法。僅支援在使用@WebService
註解的類上使用@WebMethod
註解。- operationName:指定與此方法相匹配的wsdl:operation 的名稱。預設值為 Java 方法的名稱。(字串)
- action:定義此操作的行為。對於 SOAP 繫結,此值將確定 SOAPAction 頭的值。預設值為 Java 方法的名稱。(字串)
- exclude:指定是否從 Web Service 中排除某一方法。預設值為 false。(布林值)
-
@WebParam:用於定製從單個引數至
Web Service
訊息部件和XML
元素的對映。
為了有個直觀感受,大家可以看看以下這個wsdl檔案,對應以上各註解屬性的值(加了字首oKong
)。
//@WebService 屬性示例
@WebService(targetNamespace = 'http://www.lqdev.cn/webservice' ,name = "oKongName", serviceName="oKongServiceName", portName = "oKongPortName",endpointInterface="cn.lqdev.learning.springboot.cxf.service.AuthorService")
//@webMethod @WebParam 常用屬性示例
@WebMethod(operationName="oKongOperationName",action="oKongAction")
String getAuthorName(@WebParam(name = "paramName") String name);
標記的有點花,⊙﹏⊙‖∣。大家可以自己對照下。
SpringBoot整合CXF例項
接下來,我們以一個簡單的示例來演示下,如何釋出服務及如何進行服務呼叫。
服務端構建
建立一個工程:spring-boot-cxf-service
.
0.引入CXF的POM檔案
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-spring-boot-starter-jaxws</artifactId>
<version>3.2.5</version>
</dependency>
1.建立實體,按JAX-WS
規範,建立介面及其實現類。
AuthorDto.java
/**
* 作者資訊實體
* @author oKong
*
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class AuthorDto {
String name;
List<String> hobby;
String birthday;
String description;
Sex sex;
}
Sex.java
性別列舉類
/**
* 性別列舉類
* @author oKong
*
*/
public enum Sex {
MALE("male"),
FEMALE("female");
String value;
Sex(String value) {
this.value = value;
}
public String value() {
return value;
}
public static Sex fromValue(String v) {
for (Sex c : Sex.values()) {
if (c.value.equals(v)) {
return c;
}
}
throw new IllegalArgumentException(v);
}
}
AuthorService.java
介面類
/**
* 建立服務介面
* @author oKong
*
*/
@WebService(targetNamespace = WsConst.NAMESPACE_URI ,name = "authorPortType")
public interface AuthorService {
/**
* 根據名稱獲取作者資訊
* @author 作者:oKong
*/
@WebMethod(operationName="getAuthorByName")
AuthorDto getAuthor(@WebParam(name = "authorName") String name);
/**
* 獲取作者列表資訊
* @author oKong
*/
@WebMethod
List<AuthorDto> getAuthorList();
/**
* 返回字串測試
* @author oKong
*/
String getAuthorString(@WebParam(name = "authorName")String name);
}
AuthorServiceImpl.java
介面實現類
@WebService(
targetNamespace = WsConst.NAMESPACE_URI, //wsdl名稱空間
name = "authorPortType", //portType名稱 客戶端生成程式碼時 為介面名稱
serviceName = "authorService", //服務name名稱
portName = "authorPortName", //port名稱
endpointInterface = "cn.lqdev.learning.springboot.cxf.service.AuthorService")//指定釋出webservcie的介面類,此類也需要接入@WebService註解
public class AuthorServiceImpl implements AuthorService{
@Override
public AuthorDto getAuthor(String name) {
AuthorDto author = new AuthorDto();
author.setBirthday("1990-01-23");
author.setName("姓名:" + name);
author.setSex(Sex.MALE);
author.setHobby(Arrays.asList("電影","旅遊"));
author.setDescription("描述:一枚趔趄的猿。現在時間:" + new Date().getTime());
return author;
}
@Override
public List<AuthorDto> getAuthorList() {
List<AuthorDto> resultList = new ArrayList<>();
AuthorDto author = new AuthorDto();
author.setBirthday("1990-01-23");
author.setName("姓名:oKong");
author.setSex(Sex.MALE);
author.setHobby(Arrays.asList("電影","旅遊"));
author.setDescription("描述:一枚趔趄的猿。現在時間:" + new Date().getTime());
resultList.add(author);
resultList.add(author);
return resultList;
}
@Override
public String getAuthorString(String name) {
AuthorDto author = getAuthor(name);
return author.toString();
}
}
注意:相關注解可以檢視章節:常用註解介紹
主要是介面實現類的@WebService
對應屬性值都要wsdl檔案的對映關係。
@WebService(
targetNamespace = WsConst.NAMESPACE_URI, //wsdl名稱空間
name = "authorPortType", //portType名稱 客戶端生成程式碼時 為介面名稱
serviceName = "authorService", //服務name名稱
portName = "authorPortName", //port名稱
endpointInterface = "cn.lqdev.learning.springboot.cxf.service.AuthorService")//指定釋出webservcie的介面類,此類也需要接入@WebService註解
2.建立常量類,配置類,設定訪問uri路徑等。
WsConst.java
/**
* 常量類
* @author oKong
*
*/
public class WsConst {
public static final String NAMESPACE_URI = "http://www.lqdev.cn/webservice";
}
CxfWebServiceConfig.java
/**
* cxf配置類
* @author oKong
*
*/
@Configuration
public class CxfWebServiceConfig {
//這裡需要注意 由於springmvc 的核心類 為DispatcherServlet
//此處若不重新命名此bean的話 原本的mvc就被覆蓋了。可檢視配置類:DispatcherServletAutoConfiguration
//一種方法是修改方法名稱 或者指定bean名稱
//這裡需要注意 若beanName命名不是 cxfServletRegistration 時,會建立兩個CXFServlet的。
//具體可檢視下自動配置類:Declaration org.apache.cxf.spring.boot.autoconfigure.CxfAutoConfiguration
//也可以不設定此bean 直接通過配置項 cxf.path 來修改訪問路徑的
@Bean("cxfServletRegistration")
public ServletRegistrationBean dispatcherServlet() {
//註冊servlet 攔截/ws 開頭的請求 不設定 預設為:/services/*
return new ServletRegistrationBean(new CXFServlet(), "/ws/*");
}
/**
* 申明業務處理類 當然也可以直接 在實現類上標註 @Service
* @author oKong
*/
@Bean
public AuthorService authorService() {
return new AuthorServiceImpl();
}
/*
* 非必要項
*/
@Bean(name = Bus.DEFAULT_BUS_ID)
public SpringBus springBus() {
SpringBus springBus = new SpringBus();
return springBus;
}
/*
* 釋出endpoint
*/
@Bean
public Endpoint endpoint(AuthorService authorService) {
EndpointImpl endpoint = new EndpointImpl(springBus(), authorService);
endpoint.publish("/author");//釋出地址
return endpoint;
}
}
注意事項:
- 配置
ServletRegistrationBean
時,需要注意設定方法的名稱或者bean的名稱時,不要和預設的DispatcherServlet
類重名了,會導致原先的mvc
介面無法使用,因為被覆蓋了。 - 修改訪問的路徑可以通過設定
ServletRegistrationBean
來修改,但同時,要注意需要設定bean的名稱為cxfServletRegistration
,不然會造成註冊多個CXFServlet
的。具體原因可檢視自動配置類:org.apache.cxf.spring.boot.autoconfigure.CxfAutoConfiguration
。
所以,修改訪問路徑還可以通過配置項:cxf.path
來設定。其預設的訪問url為:/services
3.建立啟動類,同時啟動應用。
/**
* cxf服務釋出示例
* @author oKong
*
*/
@SpringBootApplication
@Slf4j
public class CxfServiceApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(CxfServiceApplication.class, args);
log.info("spirng-boot-cxf-service-chapter34啟動!");
}
}
啟動後,可以從控制檯看見可以訪問的url路徑資訊。
2018-11-10 22:06:40.898 INFO 46364 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean : Mapping servlet: 'CXFServlet' to [/ws/*]
2018-11-10 22:06:40.899 INFO 46364 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean : Mapping servlet: 'dispatcherServlet' to [/]
自此,webService
釋出成功了。
客戶端呼叫
建立一個客戶端工程:spring-boot-cxf-client
。
0.引入cxf依賴。
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-spring-boot-starter-jaxws</artifactId>
<version>3.2.5</version>
</dependency>
1.建立wsdl
檔案,同時利用外掛:cxf-codegen-plugin
建立相關類。
<!-- cxf-codegen-plugin -->
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<version>3.2.5</version>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<configuration>
<sourceRoot>${project.build.directory}/generated/cxf</sourceRoot>
<wsdlOptions>
<wsdlOption>
<wsdl>src/main/resources/wsdl/author.wsdl</wsdl>
<wsdlLocation>classpath:wsdl/author.wsdl</wsdlLocation>
</wsdlOption>
</wsdlOptions>
</configuration>
<goals>
<goal>wsdl2java</goal>
</goals>
</execution>
</executions>
</plugin>
將wsdl
檔案,放入main/resources/wsdl
目錄下。之後執行:mvn generate-sources
命令,就會自動建立相應的類檔案了。拷貝相應的類檔案至src/java
目錄下即可。或者直接指定sourceRoot
也是可以的。
2.建立呼叫的配置類,這裡演示兩種方式。
WsConst.java
/**
* 常量類
* @author oKong
*
*/
public class WsConst {
public static final String NAMESPACE_URI = "http://www.lqdev.cn/webservice";
public static final String SERVICE_ADDRESS= "http://127.0.0.1:8080/ws/author?wsdl";
}
CxfClinetConfig.java
/**
* 配置類
*
* @author oKong
*
*/
@Configuration
public class CxfClientConfig {
/**
* 以介面代理方式進行呼叫 AuthorPortType介面
*/
@Bean("cxfProxy")
public AuthorPortType createAuthorPortTypeProxy() {
JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean();
jaxWsProxyFactoryBean.setServiceClass(AuthorPortType.class);
jaxWsProxyFactoryBean.setAddress(WsConst.SERVICE_ADDRESS);//服務地址:http://127.0.0.1:8080/ws/autho
return (AuthorPortType) jaxWsProxyFactoryBean.create();
}
/*
* 採用動態工廠方式 不需要指定服務介面
*/
@Bean
public Client createDynamicClient() {
JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance();
Client client = dcf.createClient(WsConst.SERVICE_ADDRESS);
return client;
}
}
注意:除了使用JaxWsProxyFactoryBean
和JaxWsDynamicClientFactory
呼叫外,還可以直接使用自動生成的AuthorService
類直接呼叫的,此類繼承至javax.xml.ws.Service
。
如:
/*
* 直接呼叫
*/
@Bean("jdkProxy")
public AuthorPortType createJdkService() {
AuthorService authorService = new AuthorService();
return authorService.getAuthorPortName();
}
其實,最後都是使用AuthorPortType
進行呼叫的。
3.建立控制層,進行呼叫示例。
/**
* 呼叫示例
* @author oKong
*
*/
@RestController
@RequestMapping("/cxf")
public class DemoController {
@Autowired
Client client;
@Autowired
@Qualifier("cxfProxy")
AuthorPortType authorPort;
@GetMapping("/getauthorstring")
public String getAuthorString(String authorName) {
return authorPort.getAuthorString(authorName);
}
@GetMapping("/getauthor")
public AuthorDto getAuthor(String authorName) {
return authorPort.getAuthorByName(authorName);
}
@GetMapping("/getauthorlist")
public List<AuthorDto> getAuthorList() {
return authorPort.getAuthorList();
}
@GetMapping("/dynamic/{operation}")
public Object getAuthorStringByDynamic(@PathVariable("operation")String operationName, String authorName) throws Exception {
//這裡就簡單的判斷了
Object[] objects = null;
// client.getEndpoint().getBinding().getBindingInfo().getOperations()
if ("getAuthorList".equalsIgnoreCase(operationName)) {
objects = client.invoke(operationName);
} else if ("getAuthorString".equalsIgnoreCase(operationName)) {
objects = client.invoke(operationName, authorName);
} else if ("getAuthorByName".equalsIgnoreCase(operationName)) {
objects = client.invoke(operationName, authorName);
} else {
throw new RuntimeException("無效的呼叫方法");
}
return objects != null && objects.length > 0 ? objects[0] : "返回異常";
}
}
4.編寫啟動類,同時制定應用埠為:8090。
/**
* cxf-客戶端呼叫示例
*
* @author oKong
*
*/
@SpringBootApplication
@Slf4j
public class CxfClientApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(CxfClientApplication.class, args);
log.info("spring-boot-cxf-client-chapter34啟動!");
}
}
埠號配置:
server.port=8090
5.啟動應用,依次訪問。檢視是否呼叫成功。
其他的就不一一貼圖了,可自行訪問下。
異常捕獲
Cxf
發生異常時,會統一丟擲:org.apache.cxf.interceptor.Fault
類的,所以想要捕獲異常,可以在統一異常裡面進行捕獲,關於統一異常處理,可以檢視文章:第八章:統一異常、資料校驗處理。
自定義攔截器
CXF的攔截器分為兩種:InInterceptor
和OutInterceptor
。顯而易見,InInterceptor
可以處理soap請求訊息,OutInterceptor
可以處理soap響應訊息。其攔截器都繼承至AbstractPhaseInterceptor<Message>
介面類,而且,本身也自帶了很多的攔截器,可以自行新增看看,比如日誌攔截器之類的:LoggingInInterceptor
和LoggingOutInterceptor
。
請求流程圖:
攔截器鏈的階段:
輸入攔截器鏈有如下幾個階段,這些階段按照在攔截器鏈中的先後順序排列。
輸出攔截器鏈有如下幾個階段,這些階段按照在攔截器鏈中的先後順序排列。
具體名稱,可檢視:org.apache.cxf.phase.Phase
。
現在,我們自定義個實現攔截器,實現請求時header需要帶上特定引數,或者大家可不寫一些安全校驗的自定義攔截器,本例只是簡單的示例。
服務端攔截器
1.檢驗攔截器:CheckAuthInterceptor.java
/**
* 簡易-安全校驗攔截器
*
* @author oKong
*
*/
@Slf4j
public class CheckAuthInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
public CheckAuthInterceptor() {
super(Phase.PRE_INVOKE);// 攔截節點:呼叫之前
}
@Override
public void handleMessage(SoapMessage message) throws Fault {
log.info("檢驗攔截器開始檢驗:{}", message);
// 處理方法
List<Header> headers = message.getHeaders();
// 判斷是否存header
// 檢查headers是否存在
if (headers == null | headers.size() <
相關推薦
SpringBoot | 第三十四章:CXF構建WebService服務
前言
上一章節,講解了如何使用Spring-WS構建WebService服務。其實,建立WebService的方式有很多的,今天來看看如何使用apache cxf來構建及呼叫WebService服務。
一點知識
何為Apache-CXF
Apache C
SpringBoot | 第三十二章:事件的釋出和監聽
前言
今天去官網檢視spring boot資料時,在特性中看見了系統的事件及監聽章節。想想,spring的事件應該是在3.x版本就釋出的功能了,並越來越完善,其為bean和bean之間的訊息通訊提供了支援。比如,我們可以在使用者註冊成功後,傳送一份註冊成功的郵件至使用者郵箱或者傳送簡訊。使用事件其實最
第三十四章:Filter過濾器
作者:java_wxid Filter,什麼是過濾器? 1.Filter過濾器是javaWeb的三大元件之一, 2.三大元件分別是:Servlet程式,Filter過濾器,Listener監聽器。 3.Filter是介面。 4.Filter的作用是:攔截請求,過濾響應。
Filter
SpringBoot | 第三十五章:Mybatis的整合和使用
前言
最近收到公眾號留言說,單純的Mybatis的整合和使用。前面在第九章:Mybatis-plus的整合和使用介紹了基於mybatis-plus的整合和使用。後者也只是對mybatis進行了功能增強,原本的用法都是沒有變化的。那今天就來簡單介紹瞭如何springboot中如何整合和使用Mybatis吧。
“全棧2019”Java第三十四章:可變引數列表
難度
初級
學習時間
10分鐘
適合人群
零基礎
開發語言
Java
開發環境
JDK v11
IntelliJ IDEA v2018.3
文章原文連結
“全棧2019”Java第三十四章:可變引數列表
下一章
“全棧2
SpringBoot | 第三十一章:MongoDB的整合和使用
前言
上一章節,簡單講解了如何整合Spring-data-jpa。本章節,我們來看看如何整合NoSQL的Mongodb。mongodb是最早熱門非關係資料庫的之一,使用也比較普遍。最適合來儲存一些非結構資料了,適合對大量或者無固定格式的資料進行儲存,比如:日誌、快取等。
一點知識
以下部分關於Mongo
“全棧2019”Java多線程第三十四章:超時自動喚醒被等待的線程
開發環境 工程 tell 開發語言 java 適合 多線程 學習計劃 如何獲取 難度
初級
學習時間
10分鐘
適合人群
零基礎
開發語言
Java
開發環境
JDK v11
IntelliJ IDEA v2018.3
文章原文鏈接
“全棧2019”Java多線程第三十
SpringBoot | 第三十七章:整合Jasypt實現配置項加密
前言
近期在進行專案安全方面評審時,質量管理部門有提出需要對配置檔案中的敏高檔案進行加密處理,避免了資訊洩露問題。想想前段時間某
SpringBoot | 第三十八章:基於RabbitMQ實現訊息延遲佇列方案
前言
前段時間在編寫通用的訊息通知服務時,由於需要實現類似通知失敗時,需要延後幾分鐘再次進行傳送,進行多次嘗試後,進入定時傳送機制。此機制,在原先對接銀聯支付時,銀聯的非同步通知也是類似的,在第一次通知失敗後,支付標準服務會重發,最多傳送五次,每次的間隔時間為1、4、8、16分鐘等。本文就簡單講解下使用Ra
4.4 Spring-boot之idea熱部署 > 我的程式猿之路:第三十四章
1、點選: file ,Settings ,Build ,Execution,Deplment,compiler
然後記得apply,ok。 2、組合鍵:Shift+ALT+Ctrl+/ ,選擇“Registry”,找到“complier.automake.allow.when.app.running”
一起talk C栗子吧(第三十四回:C語言實例--巧用溢出計算最值)
gcc 空間 代碼 讓我 計算 max value 其他 存儲 點擊
各位看官們。大家好,上一回中咱們說的是巧用移位的樣例,這一回咱們說的樣例是:巧用溢出計算最值。
閑話休提,言歸正轉。讓我們一起talk C栗子吧!
大家都知
python3 第三十四章 - 聊聊File對象
python 空白 返回 next als 使用 對象 序列 其中 file 對象使用 open 函數來創建,下表列出了 file 對象常用的函數:
序號方法及描述實例
1
file.close()關閉文件。關閉後文件不能再進行讀寫操作。
第三十一章:檔案上傳&下載
作者:java_wxid
檔案的上傳介紹 檔案的上傳和下載功能是很多系統中非常常見的功能。非常的重要。
檔案的上傳 1、首先需要一個form表單 2、然後在表單內有input type=”file” 3、提交的方式必須是method=”POST” 4、enctype="multi
第三十五章:JSON格式
作者:java_wxid 什麼是JSON? 1.介紹:JSON (JavaScript Object Notation) 是一種輕量級的資料交換格式。 易於人閱讀和編寫。同時也易於機器解析和生成。 JSON採用完全獨立於語言的文字格式,但是也使用了類似於C語言家族的習慣(包括C, C++
“全棧2019”Java第三十一章:二維陣列和多維陣列詳解
難度
初級
學習時間
10分鐘
適合人群
零基礎
開發語言
Java
開發環境
JDK v11
IntelliJ IDEA v2018.3
文章原文連結
“全棧2019”Java第三十一章:二維陣列和多維陣列詳解
下一章
“全棧2019”Java第三十二章:增強for迴
“全棧2019”Java第三十一章:二維數組和多維數組詳解
inf 基礎 公眾號 方式 www 適合 語法 文章 公眾 難度
初級
學習時間
10分鐘
適合人群
零基礎
開發語言
Java
開發環境
JDK v11
IntelliJ IDEA v2018.3
文章原文鏈接
“全棧2019”Java第三十一章:二維數組和多維數組詳
“全棧2019”Java第三十五章:面向物件
難度
初級
學習時間
10分鐘
適合人群
零基礎
開發語言
Java
開發環境
JDK v11
IntelliJ IDEA v2018.3
文章原文連結
“全棧2019”Java第三十五章:面向物件
下一章
“全棧2019”Java第三十六章:類
學習小組
加入同步
“全棧2019”Java第三十二章:增強for迴圈Foreach語法
難度
初級
學習時間
10分鐘
適合人群
零基礎
開發語言
Java
開發環境
JDK v11
IntelliJ IDEA v2018.3
文章原文連結
“全棧2019”Java第三十二章:增強for迴圈Foreach語法
“全棧2019”Java第三十七章:類與欄位
難度
初級
學習時間
10分鐘
適合人群
零基礎
開發語言
Java
開發環境
JDK v11
IntelliJ IDEA v2018.3
文章原文連結
“全棧2019”Java第三十七章:類與欄位
下一章
“全棧2019”Java第三十八章:類與方法
學習小組
加
“全棧2019”Java第三十八章:類與方法
難度
初級
學習時間
10分鐘
適合人群
零基礎
開發語言
Java
開發環境
JDK v11
IntelliJ IDEA v2018.3
文章原文連結
“全棧2019”Java第三十八章:類與方法
下一章
“全棧201