java创建webservice client

    xiaoxiao2022-07-13  215

    java的webservice实现有多种方式,可用的工具也有一些。之前对这块的只是比较缺乏,以至于一上来就一直看spring webservice.花费了几天后发现和要用的功能不符,就···

    当前学习的需求是webservice client。因此整篇文章用来说明java webserviceclient的创建过程。

    首先使用java自带的soapconnection实现。那首先具体的client访问流程为

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    SOAPConnection connection = null;

     

            try {

                SOAPConnectionFactory sfc = SOAPConnectionFactory.newInstance();

                connection = sfc.createConnection();

                SOAPMessage soapMessage = ObjectToSoapXml(object, nsMethod, nsName);

     

                URL endpoint = new URL(new URL(url),

                        "",

                        new URLStreamHandler() {

                            @Override

                            protected URLConnection openConnection(URL url) throws IOException {

                                URL target = new URL(url.toString());

                                URLConnection connection = target.openConnection();

                                // Connection settings

                                connection.setConnectTimeout(120000); // 2 min

                                connection.setReadTimeout(60000); // 1 min

                                return(connection);

                            }

                        });

     

                SOAPMessage response = connection.call(soapMessage, endpoint);

                 

            } catch (Exception e) {

                 

            }

     这其中首先创建soapconnection调用call方法向server端发送请求,call的两个参数一个是发送的消息soapmessage,一个是服务器端地址。

    那这里的关键是soapmessage的封装,那在java中,信息一般采用对象的形式存储。问题就是怎样把含有信息的对象封装成soapMessage.我采用的方法是

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    private static<T> SOAPMessage ObjectToSoapXml(T object, String nsMethod, String nsName) {

            SOAPMessage soapMessage = null;

     

            try {

                MessageFactory messageFactory = MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL);

                soapMessage = messageFactory.createMessage();

                SOAPPart soapPart = soapMessage.getSOAPPart();

                // SOAP Envelope

                SOAPEnvelope envelope = soapPart.getEnvelope();

                envelope.setPrefix("SOAP-ENV");

                envelope.addNamespaceDeclaration("ns1", nsMethod);

     

                // SOAP Body

                SOAPBody soapBody = envelope.getBody();

                soapBody.setPrefix("SOAP-ENV");

     

                soapBody.addDocument(jaxbObjectToXML(object, nsMethod, nsName));//将body中的类通过document的形式写入

                soapMessage.saveChanges();

            } catch (SOAPException e) {

                e.printStackTrace();

                 

            }

     

            return soapMessage;

        }

      使用messagefactory创建soapmessage,这里有一点要注意SOAPConstants.SOAP_1_1_PROTOCOL,使用这个参数的原因是要指定soapmessage的content-type为text/xml,charset=utf-8,那其他的可再参考其他常量。那创建的soapmessage就包含soapenvelop了,可添加前缀和命名空间。接下来就是在soapbody中添加要传递的信息对象。一开始看到的各种例子都是一个元素一个元素的添加。可扩展性太差了。一直考虑将整个对象添加进去。采用方式是soapbody adddocument的方式。那就要把信息对象转换成org.w3c.dom.Document对象。接下来是转换方式

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    private static<T> Document jaxbObjectToXML(T emp, String nsMethod, String nsName) {

            try {

                JAXBContext context = JAXBContext.newInstance(emp.getClass());

                // Create the Document

                DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

                DocumentBuilder db = dbf.newDocumentBuilder();

                Document document = db.newDocument();

     

                // Marshal the Object to a Document

                Marshaller marshaller = context.createMarshaller();

                marshaller.marshal(emp, document);

                if(null != document) {

                    document.renameNode(document.getFirstChild(), nsMethod, nsName);

                }

     

                return document;

            } catch (Exception e) {

                logger.error(e.toString(), e);

            }

            return null;

        }

      使用jaxb将object转成xml,marshal方法可直接实现对象到document。我在这里遇到的一个问题是,对象到xml的时候,我的xml要求根元素有前缀。没知识实在不好添加。最终找到实现方式是转换成的document获取根元素,通过getfirstchild的方式,然后对根元素重命名 renameNode,这里的问题是这个方法的后两个参数一个是命名空间,一个是重命名后节点名称。我要使用的是含有前缀,无命名空间。其实这样说就是没知识了。前缀和命名空间应该是对应的,命名空间和前缀应一起设置。只是命名空间并不显示。若只设置前缀,无命名空间则会报错。那这里问题就愉快的解决了,此时是完成了对象封装成soapmessage,就可以通过soapconnection向服务端发消息了。

    那消息发送出去服务端返回结果是不是还要处理一下呢?当然可以通过元素逐级获取的方式获取你要的元素。同样扩展性太差。我采用的方式同样是把soapbody中的内容实现到对象的对应。

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    public static<T> T parseSoapMessage(SOAPMessage reqMsg, T object, String name) {

            try {

                reqMsg = removeUTFBOM(reqMsg);

                SOAPBody soapBody = reqMsg.getSOAPBody();

                Document document = soapBody.extractContentAsDocument();//获取返回信息中的消息体

                document.renameNode(document.getFirstChild(), null, name);//根节点去掉前缀

                JAXBContext jc = JAXBContext.newInstance(object.getClass());

                Unmarshaller unmarshaller = jc.createUnmarshaller();

                object = (T)unmarshaller.unmarshal(document);

            }catch(Exception e) {

                logger.error(e.toString(), e);

            }

     

            return object;

        }

      

    大问题来了,调用soapconnection返回的soapmessage,直接调用getsoapbody报错了。几番查看,是返回的结果是utf-8 BOM滴,额,这个处理没有找到好的方式。最终也只是将soapmessage转成string将BOM去掉之后再转换回来。因之前对象到soapmessage转换时使用document,那现在也考虑这种方式并且可行了。那注意抽取出来的document呢我这边还是含有前缀的,所以使用renameNode做了一下去除前缀的处理,然后使用unmarshal将document嗨皮的转成对象了。终于完成了。

    去BOM的方式

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    private static SOAPMessage removeUTFBOM(SOAPMessage soapMessage) {

            ByteArrayOutputStream baos = null;

            try

            {

                baos = new ByteArrayOutputStream();

                soapMessage.writeTo(baos);

                String soapString = baos.toString();

                if (baos.toString().startsWith("\uFEFF")) {

                    soapString = soapString.substring(1);

                    InputStream is = new ByteArrayInputStream(soapString.getBytes());

                    soapMessage = MessageFactory.newInstance().createMessage(null, is);

                }

     

            } catch (SOAPException e) {

                logger.error(e.toString(), e);

            } catch (IOException e) {

                logger.error(e.toString(), e);

            }

     

            return soapMessage;

        }

      最后还有一点就是xml对应bean的定义

    我采取的方式是在类上注解

    @XmlRootElement(name = "name") //声明为根元素,根元素名字 @XmlAccessorType(XmlAccessType.FIELD)

    然后在各个元素上

    @XmlElement(name = "elementname", nillable = true)

    指定该属性对应xml中元素的名字,使用nillable属性是因为,若此属性为空的话,相应的xml元素便不存在,指定此属性为true,则为空的属性也会显示。

    再就是为根元素的类中含有其他对象时,其他对象的声明方式

    首先在类上声明   @XmlAccessorType(XmlAccessType.FIELD)

    相应属性上声明      @XmlElement(name = "elementname", nillable = true)

    再就是有些属性为list

    @XmlElementWrapper(name="ListName") @XmlElement(name="ListelementName", nillable = true)

    wrapper指定list的名字,接下来指定list中各个元素的名字。

    呼~ 终于走完了

    最新回复(0)