JAXB2.0

WEB Service CXF实战3-服务实现

版权声明:转载时请务必以超链接形式标明文章原文出处和作者信息
个人声明:文章中的内容如果引用了您的原创,给您带来了负面的影响,请联系我
技术支持:如果有任何文章相关的技术问题,请联系bldmickey@gmail.com

JAX-WS数据映射参考:
http://www.ibm.com/developerworks/cn/webservices/ws-tip-jaxwsrpc2.html
http://www.ibm.com/developerworks/cn/webservices/ws-tip-jaxwsrpc.html
概述JAX-WS 2.0 是 JAX-RPC 1.1 的后续版本,通过使用 JAXB (Java Architecture for XML Binding),一种 JCP 定义的技术,它对数据映射方法进行了改进。
       尽管 JAX-WS 2.0 中的一些方面是在 JAX-RPC 1.1 的基础上进行的改进,但其他的部分却是革命性的。例如,JAX-WS 没有提供 XML 模式和 Java 之间的映射,而这是 JAX-RPC 1.1 中的一个重要特性。相反,JAX-WS 使用了另一种 JCP 定义的技术 JAXB (Java Architecture for XML Binding) 2.0,为其完成数据映射。JAX-WS 仅仅提供了 Web 服务调用模型。它不再关心表示应用程序数据的 Java Bean,而仅仅关注于将其提供给目标 Web 服务。

简单类型映射 1. JAX-RPC 1.1 JAXB 2.0 XML 简单类型映射的差异

类型

JAX-RPC 1.1

JAXB 2.0

xsd:anySimpleType

java.lang.String

java.lang.Object

xsd:duration

java.lang.String

javax.xml.datatype.Duration(新的类型)

xsd:dateTime

java.util.Calendar

javax.xml.datatype.XMLGregorianCalendar(新的类型)

xsd:time

java.util.Calendar

javax.xml.datatype.XMLGregorianCalendar

xsd:date

java.util.Calendar

javax.xml.datatype.XMLGregorianCalendar

xsd:gYearMonth

java.lang.String

javax.xml.datatype.XMLGregorianCalendar

xsd:gYear

java.lang.String

javax.xml.datatype.XMLGregorianCalendar

xsd:gMonthDay

java.lang.String

javax.xml.datatype.XMLGregorianCalendar

xsd:gMonth

java.lang.String

javax.xml.datatype.XMLGregorianCalendar

xsd:gDay

java.lang.String

javax.xml.datatype.XMLGregorianCalendar

xsd:anyURI

java.net.URI

java.lang.String

xsd:NMTOKENS

java.lang.String[]

java.util.List<java.lang.String>

xsd:IDREF

java.lang.String

java.lang.Object

xsd:IDREFS

java.lang.String[]

java.util.List<java.lang.Object>

xsd:ENTITY

not supported

java.lang.String

xsd:ENTITIES

not supported

java.util.List<java.lang.String>


在 JAX-RPC 和 JAXB 之间,简单类型映射的纯 Java 方面几乎相同,但是 JAXB 映射还使用了新的 Java 注释特性。
在 JAX-RPC 1.1 所生成的 Java Bean 中,您无法区分下面的差别:

  • 元素字段和属性字段
  • 从 minOccurs="0" type="xsd:int" 映射的字段和从 nillable="true" type="xsd:int" 映射的字段
  • 从 type="xsd:string" 映射的字段和从 type="xsd:string" minOccurs="0" 映射的字段

   但由于 JAXB 使用了新的 Java 注释,现在您可以区分出它们之间的差别。@XmlElement 和 @XmlAttribute 注释具有一些选项。其中与本文相关的选项包括:

  • Required:该元素是否必须存在?例如,minOccurs 是否不等于 1?
  • Nillable:该字段是否包含 nillable="true" 属性
XML complexType 元素和属性
<xsd:element name="intField" type="xsd:int"/>
<xsd:element name="intMinField" type="xsd:int" minOccurs="0"/>
<xsd:element name="stringMinField" type="xsd:string" minOccurs="0"/>
<xsd:element name="stringNilField" type="xsd:string" nillable="true"/>

<xsd:attribute name="intAttr" type="xsd:int"/>
<xsd:attribute name="intAttrReq" type="xsd:int" use="required"/>
通过 JAXB 2.0 映射为 Java Bean 属性
protected int intField;
protected Integer intMinField;
@XmlElement(required = true)
protected String stringMinField;
@XmlElement(required = true, nillable = true)
protected String stringNilField;

@XmlAttribute
protected Integer intAtt;
@XmlAttribute(required = true)
protected int intAttReq;



数组映射    在 JAX-RPC 和 JAXB 之间,从 XML 到 Java 的数组映射存在着差异,这是因为 JAXB 使用了新的泛型 Java 特性.
    注意访问器方法中的差别。JAX-RPC 映射遵循 Java Bean 数组访问器的严格定义。JAXB 映射并没有映射为数组,所以有些不同。getIntArrayField 返回一个引用,不仅仅是快照,而是实际的列表。因此,您对返回的列表所做的任何修改都将出现在属性中。这就是为什么对于数组属性没有 set 方法的原因。

Address.java

@XmlElement(required = true)

protected List<String> street;

    public List<String> getStreet() {

        if (street == null) {

            street = new ArrayList<String>();

        }

        return this.street;

    }



复杂类型的映射     在 JAX-RPC 和 JAXB 中,对 complexType 的映射几乎相同,除了这些字段在 JAX-RPC 中是私有的,而在 JAXB 中是保护的,并且 JAXB 映射添加了相应的注释对字段顺序进行描述。

Address.java

@XmlAccessorType(XmlAccessType.FIELD)

@XmlType(name = "Address", propOrder = {

    "name",

    "street",

    "city",

    "country"

})

public class Address {

    @XmlElement(required = true)

    protected String name;

    @XmlElement(required = true)

    protected List<String> street;

    @XmlElement(required = true)

    protected String city;

    @XmlElement(required = true)

    protected String country;



ObjectFactory 类      JAXB 生成了一个文件 ObjectFactory,而 JAX-RPC 则没有。每个包含 Java Bean 的目录都会有一个 ObjectFactory。对于在模式相应的命名空间中定义的每种类型,ObjectFactory 类都为该类型提供了一种创建方法。对于每个元素,ObjectFactory 类提供了一种创建元素的方法,该方法返回一个 javax.xml.bind.JAXBElement<Type>。例如,下面JAXBElement<BookOrderResponse>的例子,这个ObjectFactory类具有一个返回BookOrder的实例和一个返回 JAXBElement<BookOrder> 的实例的方法。您仍然可以直接实例化该目录中的 Java Bean,但最好是使用这个工厂。

ObjectFactory.java

package org.example.cxfstp04;


import javax.xml.bind.JAXBElement;

import javax.xml.bind.annotation.XmlElementDecl;

import javax.xml.bind.annotation.XmlRegistry;

import javax.xml.namespace.QName;


@XmlRegistry

public class ObjectFactory {


    private final static QName _BookOrderResponse_QNAME = new QName("http://www.example.org/cxfstp04/", "BookOrderResponse");

    private final static QName _BookOrder_QNAME = new QName("http://www.example.org/cxfstp04/", "BookOrder");


    public ObjectFactory() {

    }

    public USAddress createUSAddress() {

        return new USAddress();

}

// 返回BookOrder的方法

    public BookOrder createBookOrder() {

        return new BookOrder();

    }

    public BookOrderResponse createBookOrderResponse() {

        return new BookOrderResponse();

    }

    public Book createBook() {

        return new Book();

    }

    public UKAddress createUKAddress() {

        return new UKAddress();

    }

    public BriefUSAddress createBriefUSAddress() {

        return new BriefUSAddress();

    }

    public Address createAddress() {

        return new Address();

    }

    @XmlElementDecl(namespace = "http://www.example.org/cxfstp04/", name = "BookOrderResponse")

    public JAXBElement<BookOrderResponse> createBookOrderResponse(BookOrderResponse value) {

        return new JAXBElement<BookOrderResponse>(_BookOrderResponse_QNAME, BookOrderResponse.class, null, value);

    }

@XmlElementDecl(namespace = "http://www.example.org/cxfstp04/", name = "BookOrder")

//返回一个JAXBElement<BookOrder>的方法

    public JAXBElement<BookOrder> createBookOrder(BookOrder value) {

        return new JAXBElement<BookOrder>(_BookOrder_QNAME, BookOrder.class, null, value);

    }

}



实现代码实现的业务代码包括:

  • 数据类实现.
  • OrderBook服务的实现.
  • 测试的客户代码.


数据类实现

Address.java

protected List<String> street;

public List<String> getStreet() {

        if (street == null) {

            street = new ArrayList<String>();

        }

        return this.street;

    }

    // 因为street定义成List,缺省设置List没有实现,需要手工添加

    public void setStreet (int _lineN, String _street) {

    if (street == null) {

            street = new ArrayList<String>();

            street.add(0, _street);

        }

    else {

            street.add(_lineN, _street);

    }

    }

BookOrderResponse.java

    protected List<String> returninfo;

public List<String> getReturninfo() {

        if (returninfo == null) {

            returninfo = new ArrayList<String>();

        }

        return this.returninfo;

    }

    // 返回信息同样也是List实现

    public void setReturninfo (int _lineN, String _info) {

    if (returninfo == null) {

            returninfo = new ArrayList<String>();

            returninfo.add(0, _info);

        }

    else {

            returninfo.add(_lineN, _info);

    }

    }

Address.java

protected XMLGregorianCalendar orderDate;

    public XMLGregorianCalendar getOrderDate() {

        return orderDate;

    }

    public void setOrderDate(XMLGregorianCalendar value) {

        this.orderDate = value;

    }



OrderBook服务的实现

OrderBookImpl.java
OrderBook服务的实现流程:获得Web Service的所有请求信息, BookOrder Schema,并且返回相关信息
package org.example.cxfstp04;

import java.util.logging.Logger;
import javax.jws.WebMethod;
import javax.jws.WebResult;

@javax.jws.WebService(name = "OrderBook", serviceName = "cxfstp04Service",
                      portName = "OrderBookPort",
                      targetNamespace = "http://www.example.org/cxfstp04/",
                      wsdlLocation = "file:/D:/2007/CodeWorm/WebService/SourceCode/CXF/cxfstp03/wsdl/cxfstp04.wsdl" ,
                    endpointInterface = "org.example.cxfstp04.OrderBook")

public class OrderBookImpl implements OrderBook {

    private static final Logger LOG = Logger.getLogger(OrderBookImpl.class.getName());
    public org.example.cxfstp04.BookOrderResponse orderBookOper(org.example.cxfstp04.BookOrder parameters) {
        LOG.info("Executing operation orderBookOper");
        System.out.println(parameters);
        try {
            //创建BookOrderResponse返回对象
            //采用ObjectFactory方法
org.example.cxfstp04.ObjectFactory _objfactory = new org.example.cxfstp04.ObjectFactory();
               org.example.cxfstp04.BookOrderResponse _return = _objfactory.createBookOrderResponse();
            _return.setReturn("OK");
            //设置返回值的状态为”OK”
            _return.setReturn("OK");
            //获得Web Service请求中的AccountName和AccountNumber信息
            String _info_0 = "AccountName is: '" + parameters.getAccountName()+ "' and AccountNumber is : '" +parameters.getAccountNumber()+ "'";
            //获得ship地址信息
            org.example.cxfstp04.UKAddress _shipAddress = ((org.example.cxfstp04.UKAddress)(parameters.shipAddress));
            String _info_1 = _shipAddress.getName()+" "+  _shipAddress.getCity()+" "+ _shipAddress.getCountry() +" "+ _shipAddress.getPostcode();
            String _shipstreet = "";
            for (int _i=0; _i<_shipAddress.getStreet().size(); _i++)
                   _shipstreet =       _shipstreet +" "+ _shipAddress.getStreet().get(_i);
            //获得billing地址信息
            org.example.cxfstp04.BriefUSAddress _billAddress = ((org.example.cxfstp04.BriefUSAddress)(parameters.billAddress));
            String _info_2 = _billAddress.getName()+" "+_billAddress.getZip();
            String _billstreet ="";
            for (int _i=0; _i<_billAddress.getStreet().size(); _i++)
                   _billstreet +=       _billAddress.getStreet().get(_i);
            //获得书的信息
            String _info_3 = "BOOK Name, Quantity and Wholesale-prise is: '" + parameters.getBook().getTitle() + " , " +  parameters.getBook().getQuantity() + " , " + parameters.getBook().getWholesalePrice()+ "'";
            //获得书总价信息
            String _info_4 = "Total " + parameters.getTotal();
            //获得订单时间信息
            String _info_5 = "The Orderdate is: " + parameters.getOrderDate().getYear()+" , "+parameters.getOrderDate().getMonth()+" , "+parameters.getOrderDate().getDay();
            //设置所有返回信息
            _return.setReturninfo(0, _info_0);
            _return.setReturninfo(1, "shiping address is :" + _info_1+_shipstreet);
            _return.setReturninfo(2, "biliing address is :" + _info_2+_billstreet);
            _return.setReturninfo(3, _info_3);
            _return.setReturninfo(4, _info_4);
            _return.setReturninfo(5, _info_5);
            return _return;
        } catch (Exception ex) {
            ex.printStackTrace();
            throw new RuntimeException(ex);
        }
    }
}



测试的客户代码

OrderBook_OrderBookPort_Client.java
package org.example.cxfstp04;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;

public final class OrderBook_OrderBookPort_Client {
    private static final QName SERVICE_NAME = new QName("http://www.example.org/cxfstp04/", "cxfstp04Service");
    private OrderBook_OrderBookPort_Client() {
    }
    public static void main(String args[]) throws Exception {
        if (args.length == 0) {
            System.out.println("please specify wsdl");
            System.exit(1);
        }
        URL wsdlURL = null;
        File wsdlFile = new File(args[0]);
        try {
            if (wsdlFile.exists()) {
                wsdlURL = wsdlFile.toURL();
            } else {
                wsdlURL = new URL(args[0]);
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }

        Cxfstp04Service ss = new Cxfstp04Service(wsdlURL, SERVICE_NAME);
        OrderBook port = ss.getOrderBookPort();  


        System.out.println("Invoking orderBookOper...");
        // Add By bldmickey      
        org.example.cxfstp04.BookOrder _orderBookOper_parameters = new org.example.cxfstp04.BookOrder();
        // 设置帐号名和帐号
        _orderBookOper_parameters.setAccountName("Amazon.com");
        _orderBookOper_parameters.setAccountNumber(923);
        // 设置Ship地址和Billing地址
        org.example.cxfstp04.UKAddress _ship_address = new org.example.cxfstp04.UKAddress();
        org.example.cxfstp04.BriefUSAddress _bill_address = new org.example.cxfstp04.BriefUSAddress();
        _ship_address.setName("Amazon.co.uk");
        //因为街道可以有多行,代码生成的是字符串List
        _ship_address.setStreet(0,"line1: Ridgmont Road");
        _ship_address.setStreet(1,"line2: Ridgmont Road");
        _ship_address.setCity("Bedford");
        _ship_address.setCountry("United Kingdom");
        _ship_address.setPostcode("MK43 0ZA");
        _bill_address.setName("Amazon.com");
        _bill_address.setStreet(0,"line1: 1516 2nd Ave");
        _bill_address.setStreet(1,"line2: 1516 2nd Ave");
        _bill_address.setStreet(2,"line3: 1516 2nd Ave");
        _bill_address.setZip("90952");
        // 设置图书信息
        org.example.cxfstp04.Book _book = new org.example.cxfstp04.Book();
        _book.setTitle("Java Web Services");
        _book.setQuantity(300);
        _book.setWholesalePrice(24.99f);
        _orderBookOper_parameters.setShipAddress(_ship_address);
        _orderBookOper_parameters.setBillAddress(_bill_address);
        _orderBookOper_parameters.setBook(_book);
        _orderBookOper_parameters.setTotal(8997.00f);
// 对于时间JAXB缺省绑定成XMLGregorianCalendar类型,参见
//http://java.sun.com/j2se/1.5.0/docs/api/javax/xml/datatype/XMLGregorianCalendar.html
        XMLGregorianCalendar _orderdate= DatatypeFactory.newInstance().newXMLGregorianCalendar();
        _orderdate.setYear(2007);
        _orderdate.setMonth(9);
        _orderdate.setDay(8);
        _orderdate.setTimezone(0);
        _orderBookOper_parameters.setOrderDate(_orderdate);

        org.example.cxfstp04.BookOrderResponse _orderBookOper__return = port.orderBookOper(_orderBookOper_parameters);
        System.out.println("The Status of Return is : " + _orderBookOper__return.getReturn());
        for (int _i=0; _i<_orderBookOper__return.getReturninfo().size(); _i++)
               System.out.println("Return info " + _i +" is: " + _orderBookOper__return.getReturninfo().get(_i));
        //System.out.println("orderBookOper.result=" + _orderBookOper__return);

        System.exit(0);
    }

}



简单测试运行测试服务器:

  • 菜单选择Run->Open Run Dialog…->选择Java Application ->选择OrderBookServer_server_cxfstp04->Main Class设置为: org.example.cxfstp04.OrderBook_OrderBookPort_Server->选择Run


  运行服务器后显示类似如下:

……

INFO: Started SelectChannelConnector @ 0.0.0.0:9090

Server ready...


运行测试客户端

  • 菜单选择Run->Open Run Dialog…->选择Java Application ->选择OrderBookClient_client_cxfstp04->Main Class设置为: org.example.cxfstp04.OrderBook_OrderBookPort_Client->选择Run



运行测试客户端后显示如下:

INFO: Creating Service {http://www.example.org/cxfstp04/}cxfstp04Service from WSDL: file:/D:/2007/CodeWorm/WebService/SourceCode/CXF/cxfstp03/wsdl/cxfstp04.wsdl

Invoking orderBookOper...

The Status of Return is : OK

Return info 0 is: AccountName is: 'Amazon.com' and AccountNumber is : '923'

Return info 1 is: shiping address is :Amazon.co.uk Bedford United Kingdom MK43 0ZA line1: Ridgmont Road line2: Ridgmont Road

Return info 2 is: biliing address is :Amazon.com 90952line1: 1516 2nd Aveline2: 1516 2nd Aveline3: 1516 2nd Ave

Return info 3 is: BOOK Name, Quantity and Wholesale-prise is: 'Java Web Services , 300 , 24.99'

Return info 4 is: Total 8997.0

Return info 5 is: The Orderdate is: 2007 , 9 , 8




TcpMonitor

  • 运行tcpmonitor

          设置目的服务地址和端口号loclhost:9090(服务器)
          设置监听的端口为8888

  

  • 运行客户端

         修改测试客户端的wsdl参数. 客户端测试代码中,从命令行参数中获得wsdl文件的路径,从中获得请求服务的信息

OrderBook_OrderBookPort_Client.java
wsdlURL = new URL(args[0]);
Cxfstp04Service ss = new Cxfstp04Service(wsdlURL, SERVICE_NAME);

        修改wsdl中的服务地址,从原来的localhost:9090改成localhost:8888,意思将服务请求发送到tcpmonitor的8888端口,由tcpmonitor转发给9090

cxfstp04_test.wsdl
<wsdl:service name="cxfstp04Service">
    <wsdl:port name="OrderBookPort" binding="ns1:cxfstp04ServiceBinding">
      <soap:address location="http://localhost:8888/OrderBook"/>
    </wsdl:port>
</wsdl:service>

         菜单选择Run->Open Run Dialog…->选择Java Application ->选择OrderBookClient_client_cxfstp04_testing->Main Class设置为: org.example.cxfstp04.OrderBook_OrderBookPort_Client->在参数中设置程序的参数为: 修改后的cxfstp04_test.wsdl->选择Run

  请求和返回

  • TCPMonitor返回的结果
省略
省略

  服务部署详细参见:<< Eclipse Apache CXF集成.doc>>

  • 启动本地的tomcat服务器
  • 菜单选择File->New->Other…->选择SOA Tools->Deployment Profile后选择Next;
  • 在scxstp04中点击wsdl,在File Name中输入:scxstp04Deploy,如下图:选择Next;
  • 在Deployment Description选择继续;Package中选择继续;Target Server选择继续;Summary中选择Finish
  • 在wsdl目录下将创建scxstp04Deploy.deploy项,右边显示deploy文件,在文件显示框中选择Configuration Tab
  • 选择Add Target…->在弹出对话框中选择刚才启动的“Tomcat v5.5 Server at localhost”,选择OK,在Server框中将显示刚才添加的target server。
  • 选择Create Target
  • 选择Deploy Package
  • 查看Console Tab,将显示Package Deploy的状态(类似下面的log)
INFO: Creating Service {http://www.example.org/cxfstp04/}cxfstp04Service from WSDL: WEB-INF/wsdl/cxfstp04.wsdl

客户端测试

  • Web 测试wsdl

浏览器中输入: http://localhost:8081/cxfstp04/services/cxfstp04?wsdl

  

  • 考虑到目前tomcat监听在8081端口,所以修改测试wsdl文件
cxfstp04_testtomcat.wsdl
<wsdl:service name="cxfstp04Service">
    <wsdl:port name="OrderBookPort" binding="ns1:cxfstp04ServiceBinding">
      <soap:address location="http://localhost:8081/cxfstp04/services/cxfstp04"/>
    </wsdl:port>
</wsdl:service>
  • 运行客户端,输出结果
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在Spring Boot中整合JAXB,您可以按照以下步骤进行操作: 1. 添加依赖:在您的项目的pom.xml文件中,添加JAXB依赖项。 ```xml <dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> <version>2.3.1</version> </dependency> ``` 2. 创建Java类:创建要映射到XMLJava类。使用JAXB注解配置类和字段。 ```java @XmlRootElement public class Person { private String name; private int age; // 省略构造函数、getter和setter方法 } ``` 3. 创建XML转换工具类:创建一个工具类,用于执行XML转换操作。 ```java import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; import java.io.File; public class JAXBUtils { public static void marshal(Object object, File file) throws JAXBException { JAXBContext jaxbContext = JAXBContext.newInstance(object.getClass()); Marshaller marshaller = jaxbContext.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.marshal(object, file); } public static <T> T unmarshal(Class<T> clazz, File file) throws JAXBException { JAXBContext jaxbContext = JAXBContext.newInstance(clazz); Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); return clazz.cast(unmarshaller.unmarshal(file)); } } ``` 4. 配置Spring Boot:在您的Spring Boot应用程序的配置类上添加JAXB相关的配置。 ```java import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.xml.MarshallingHttpMessageConverter; import org.springframework.oxm.jaxb.Jaxb2Marshaller; @Configuration public class JAXBConfig { @Bean public Jaxb2Marshaller jaxb2Marshaller() { Jaxb2Marshaller marshaller = new Jaxb2Marshaller(); marshaller.setPackagesToScan("com.example.demo"); // 设置要扫描的包路径 return marshaller; } @Bean public HttpMessageConverter<Object> marshallingHttpMessageConverter(Jaxb2Marshaller jaxb2Marshaller) { return new MarshallingHttpMessageConverter(jaxb2Marshaller); } } ``` 5. 使用JAXB:在您的控制器或服务类中使用JAXB进行XML转换操作。 ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class PersonController { @Autowired private Jaxb2Marshaller jaxb2Marshaller; @GetMapping("/person") public Person getPerson() { Person person = new Person(); person.setName("John"); person.setAge(25); return person; } @GetMapping("/xml") public String getXml() { Person person = getPerson(); StringWriter writer = new StringWriter(); jaxb2Marshaller.marshal(person, new StreamResult(writer)); return writer.toString(); } } ``` 这样,当访问`/person`接口时,将返回一个Person对象的JSON表示。当访问`/xml`接口时,将返回Person对象的XML表示。 这就是在Spring Boot中整合JAXB的基本步骤。您可以根据需要进行扩展和自定义。希望对您有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值