1. 程式人生 > >SpringBoot | 第三十四章:CXF構建WebService服務

SpringBoot | 第三十四章:CXF構建WebService服務

前言

上一章節,講解了如何使用Spring-WS構建WebService服務。其實,建立WebService的方式有很多的,今天來看看如何使用apache cxf來構建及呼叫WebService服務。

一點知識

何為Apache-CXF

Apache CXF是一個開源的Services框架,CXF幫助您利用Frontend程式設計 API 來構建和開發Services,像JAX-WSJAX-RS。這些Services可以支援多種協議,比如:SOAPXML/HTTPRESTful HTTP或者CORBA,並且可以在多種傳輸協議上執行,比如:HTTPJMS 或者JBICXF大大簡化了 Services 的建立,同時它可以天然地和Spring進行無縫整合

最常用的是使用cxf開發web-service。本身是基於JAX-WS規範來實現的。當然,本身CXF也實現了JAX-RS規範來實現RESTFul Service

關於JAX-WS規範

JAX-WS全稱:Java API for XML-Based Web ServicesJAX-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

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 [/]

author

自此,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;
    } 
}

注意:除了使用JaxWsProxyFactoryBeanJaxWsDynamicClientFactory呼叫外,還可以直接使用自動生成的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的攔截器分為兩種:InInterceptorOutInterceptor。顯而易見,InInterceptor可以處理soap請求訊息OutInterceptor可以處理soap響應訊息。其攔截器都繼承至AbstractPhaseInterceptor<Message>介面類,而且,本身也自帶了很多的攔截器,可以自行新增看看,比如日誌攔截器之類的:LoggingInInterceptorLoggingOutInterceptor

請求流程圖:

攔截器鏈的階段:

輸入攔截器鏈有如下幾個階段,這些階段按照在攔截器鏈中的先後順序排列。

輸出攔截器鏈有如下幾個階段,這些階段按照在攔截器鏈中的先後順序排列。

具體名稱,可檢視: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