好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

详谈cxf和axis两种框架下的webservice客户端开发

客户端相比服务端,基本没啥配置。引入jar包就好。我这里为了看效果,是新建了maven工程来做客户端。调用另一个webservice服务端maven工程.

环境:jdk1.7+maven3.3.9+tomcat7

框架:spring+cxf/spring+axis(这里不是axis2,是axis)

第一种:基于cxf的客户端开发

1.引入依赖 pom.xml

这里把两种框架的依赖一次到位。就不分开引入了。

?

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

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

< project xmlns = "http://maven.apache.org/POM/4.0.0" xmlns:xsi = "http://HdhCmsTestw3.org/2001/XMLSchema-instance"

     xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd" >

     < modelVersion >4.0.0</ modelVersion >

     < groupId >com.zhanglf</ groupId >

     < artifactId >cxfClientTest</ artifactId >

     < packaging >war</ packaging >

     < version >0.0.1-SNAPSHOT</ version >

     < name >cxfClientTest Maven Webapp</ name >

     < url >http://maven.apache.org</ url >

     < dependencies >

         < dependency >

             < groupId >junit</ groupId >

             < artifactId >junit</ artifactId >

             < version >3.8.1</ version >

             < scope >test</ scope >

         </ dependency >

         <!-- 添加 Spring dependency -->

         < dependency >

             < groupId >org.springframework</ groupId >

             < artifactId >spring-core</ artifactId >

             < version >4.1.7.RELEASE</ version >

         </ dependency >

         < dependency >

             < groupId >org.springframework</ groupId >

             < artifactId >spring-beans</ artifactId >

             < version >4.1.7.RELEASE</ version >

         </ dependency >

         < dependency >

             < groupId >org.springframework</ groupId >

             < artifactId >spring-tx</ artifactId >

             < version >4.1.7.RELEASE</ version >

         </ dependency >

         < dependency >

             < groupId >org.springframework</ groupId >

             < artifactId >spring-context</ artifactId >

             < version >4.1.7.RELEASE</ version >

         </ dependency >

         < dependency >

             < groupId >org.springframework</ groupId >

             < artifactId >spring-context-support</ artifactId >

             < version >4.1.7.RELEASE</ version >

         </ dependency >

         < dependency >

             < groupId >org.springframework</ groupId >

             < artifactId >spring-web</ artifactId >

             < version >4.1.7.RELEASE</ version >

         </ dependency >

         < dependency >

             < groupId >org.springframework</ groupId >

             < artifactId >spring-webmvc</ artifactId >

             < version >4.1.7.RELEASE</ version >

         </ dependency >

         < dependency >

             < groupId >org.springframework</ groupId >

             < artifactId >spring-aop</ artifactId >

             < version >4.1.7.RELEASE</ version >

         </ dependency >

         < dependency >

             < groupId >org.springframework</ groupId >

             < artifactId >spring-aspects</ artifactId >

             < version >4.1.7.RELEASE</ version >

         </ dependency >

         < dependency >

             < groupId >org.springframework</ groupId >

             < artifactId >spring-jdbc</ artifactId >

             < version >4.1.7.RELEASE</ version >

         </ dependency >

         <!-- 添加CXF dependency -->

         < dependency >

             < groupId >org.apache.cxf</ groupId >

             < artifactId >cxf-core</ artifactId >

             < version >3.1.5</ version >

         </ dependency >

         < dependency >

             < groupId >org.apache.cxf</ groupId >

             < artifactId >cxf-rt-frontend-jaxws</ artifactId >

             < version >3.1.5</ version >

         </ dependency >

         < dependency >

             < groupId >org.apache.cxf</ groupId >

             < artifactId >cxf-rt-transports-http</ artifactId >

             < version >3.1.5</ version >

         </ dependency >

         <!-- 添加CXF dependency -->

         <!-- 另一种axis方法调用webservice依赖 -->

         < dependency >

             < groupId >org.apache.axis</ groupId >

             < artifactId >axis</ artifactId >

             < version >1.4</ version >

         </ dependency >

         < dependency >

             < groupId >org.apache.axis</ groupId >

             < artifactId >axis-jaxrpc</ artifactId >

             < version >1.4</ version >

         </ dependency >

         < dependency >

             < groupId >axis</ groupId >

             < artifactId >axis-wsdl4j</ artifactId >

             < version >1.5.1</ version >

         </ dependency >

         < dependency >

             < groupId >commons-discovery</ groupId >

             < artifactId >commons-discovery</ artifactId >

             < version >0.2</ version >

         </ dependency >

         < dependency >

             < groupId >javax.mail</ groupId >

             < artifactId >mail</ artifactId >

             < version >1.4.7</ version >

         </ dependency >

     <!-- 另一种axis方法调用webservice -->

     </ dependencies >

     < build >

         < finalName >cxfClient</ finalName >

     </ build >

</ project >

2.cxf和axis都没有spring配置

直接进入业务代码CxfClientTest.java

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

package com.zhanglf;

import org.apache.cxf.endpoint.Client;

import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory;

public class CxfClientTest {

     public static void main(String[] args) throws Exception {

         JaxWsDynamicClientFactory factory = JaxWsDynamicClientFactory.newInstance();

         //通过wsdl服务描述文件创建客户端工厂。

         Client client = factory.createClient( "http://localhost:8080/springCXFWebserviceDemo01/service/HelloWorldService?wsdl" );

         //尝试使用不带?wsdl的地址

         //Client client = factory.createClient("http://localhost:8080/springCXFWebserviceDemo01/service/HelloWorldService");

         //invoke(

         //String operationName:要调用的方法名

         //Object... params):方法的入参。可以是多个。

         Object[] objs = client.invoke( "sayHello" , "阿福" );

         //invoke方法是默认返回Object[]数组。取出数组的第一位值得值就是返回值。

         System.out.println(objs[ 0 ].toString());

     }

}

直接Run As Java Application.可以看到在控制台打出访问服务端的代码。

拓展:这里的客户端只是传了一个简单的人名,正常传入的是个String类型的xml报文。然后服务端接收报文,进行报文解析,并对信息进行crud操作。并将执行结果以报文形式发送到客户端。客户端在进行报文解析。判断执行情况以便进行下一步操作。下面我们用axis框架进行稍微接近业务的代码开发。

axis客户端代码和cxf都在一个maven工程里。我把工程结构贴出来供参考

第二种:基于axis框架的客户端开发

AxisClientTest.java的code,所有涉及的点我都在代码里说了。解释的文字比较多,代码并不多。

?

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

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

package com.zhanglf;

import java.net.URL;

import javax.xml.namespace.QName;

import javax.xml.rpc.ParameterMode;

import org.apache.axis.client.Call;

import org.apache.axis.client.Service;

import org.apache.axis.encoding.XMLType;

/**

  *

  说明:address="http://localhost:8080/HelloWorld/services/user?wsdl"

  * http://localhost:8080/HelloWorld:工程访问路径,

  * /services:此路径在web.xml里面配置cxf拦截器前置访问路径

  * /user:此路径在服务端的applicationContext.xml里配置的,要暴露给外部调用的接口,address:请求路径

  *

  * @author Administrator

  *

  */

public class AxisClientTest {

     public static void main(String[] args) throws Exception {

         // namespaceURI

         // 为:一般可以认为是<wsdl:definitions>中的targetNamespace的值,最好还是<wsdl:definitions>标签下的

         // xmlns:tns的值为准。

         // 也是webservice接口的类上注解的targetNamespace,效果如同spring中的requestMapping,用来区分请求定位到具体的那个接口。

         // 这里的命名空间对应的是接口上面的targetNamespace,而不是实现类上面的。故通过wsdl中的<wsdl:definitions

         // 里面的targetNamespace是不准的,应该以<wsdl:import 中的

         // namespace为准或者<wsdl:definitions>标签下的 xmlns:tns的值为准

         String nameSpaceURI = "com.serviceTargetName" ;

         // 指出service所在URL,这个有没有?wsdl均能正确访问。。。,这里区别于cxf,cxf是不能少这个?wsdl

         String publishUrl = "http://localhost:8080/springCXFWebserviceDemo01/service/HelloWorldService?wsdl" ;

         // 创建一个服务(service)调用(call)

         Service service = new Service();

         // 通过service创建call对象

         Call call = (Call) service.createCall();

         // 设置webservice接口地址

         call.setTargetEndpointAddress( new URL(publishUrl));

         // 你需要远程调用的方法new QName(定位类的targetnamespace,定位方法的方法名)

         /**

          * call.setOperationName(new QName("serviceTargetName", "sayHello"));

          * 方法中的QName方法的入参说明: new QName( String

          * namespaceURI-定位接口的命名空间:接口注解targetnamespace的值或者wsdl文件

          * <wsdl:definitions中的xmlns

          * :tns="com.serviceTargetName"来锁定targetnamespace的值, 这里不能用wsdl文件<wsdl:

          * definitions中的targetNamespace来确定值的原因在于这里的值来源与接口实现类上的targetNamespace注解的值

          * 。如果你接口的实现类中的targetNamespace和接口的不一样,岂不是搞错了。 String

          * localPart-接口下定位方法的方法名

          * :就是这里的抽象方法sayHello方法名,或者wsdl文件<wsdl:binding标签下<wsdl:operation

          * name="sayHello"中name的值。 )

          */

         call.setOperationName( new QName(nameSpaceURI, "sayHello" ));

         // 方法参数,如果没有参数请无视。这里如果接口没有用@WebParam(name = "name"),则会报错:Unmarshalling

         // Error: 意外的元素 (uri:"", local:"name")。所需元素为<{}arg0>

         call.addParameter( "parameterName" , XMLType.XSD_STRING, ParameterMode.IN);

         // call.addParameter(new QName(soapaction,"xxxx"), XMLType.XSD_STRING,

         // ParameterMode.IN);

         // 设置返回类型,一般用string接收

         call.setReturnType(XMLType.XSD_STRING);

         // 给方法传递参数,并且调用方法

         String name = "zhanglifeng" ;

         String temp = getXml(name);

         // 这里的obj{}是放入几个入参,完全由service提供的接口方法的入参决定,且顺序和你存放的顺序一致!一般入参为String类型的xml报文,回参也是xml报文。

         Object[] obj = new Object[] { temp };

         String result = (String) call.invoke(obj);

         System.out.println(result);

     }

     private static String getXml(String name) {

         StringBuffer sb = new StringBuffer(

                 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" );

         sb.append( "<userBean>" );

         sb.append( "<userName>" + name + "</userName>" );

         sb.append( "</userBean>" );

         return sb.toString();

     }

}

运行方法:Run As Java Application.客户端的请求的入参报文如下:

?

1

2

3

4

<? xml version = "1.0" encoding = "UTF-8" ?>

     < userBean >

         < userName >" + 入参:人名 + </ userName >

     </ userBean >

下面我把调用服务端的方法也贴出来,这样更好理解。昨天已经贴出了服务端的目录结构,我就把其中的HelloWorldImpl.java中的sayHello方法改一下。code如下:

?

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

27

package com.zlf.impl;

import javax.jws.WebService;

import org.springframework.stereotype.Component;

import org.w3c.dom.Document;

import org.w3c.dom.Node;

import com.util.XMLUtils;

import com.zlf.HelloWorld;

/**

  * 由于实现类和接口不在同一个包中。所以要加上targetNamespace属性。

  * 另外,这里的endpointInterface是实现类对应接口的全路径

  * @author Administrator

  */

@WebService (targetNamespace= "com.serviceTargetName" ,endpointInterface= "com.zlf.HelloWorld" )

@Component ( "HelloWord" ) //spring注入用

public class HelloWorldImpl implements HelloWorld {

     @Override

      public String sayHello(String str) {

         String username= "aaa" ;

         Document document = XMLUtils.parse(str);

         //首先接口开发肯定是双发都知道此方法要接受的报文格式的。我们获取报文中人名对应的节点即可。

           Node node = document.getElementsByTagName( "userName" ).item( 0 );

           if (node != null ){

               username=node.getTextContent();

           }

         return "你好," +username+ "  你已成功访问了webservice服务端!" ;

     }

}

XMLUtils.java工具类我也贴出来,这个也比较常用。

?

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

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

package com.util;

import java.io.IOException;

import java.io.StringReader;

import javax.xml.XMLConstants;

import javax.xml.parsers.DocumentBuilder;

import javax.xml.parsers.DocumentBuilderFactory;

import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;

import org.xml.sax.InputSource;

import org.xml.sax.SAXException;

/**

  * 解析器

  * @author Administrator

  *

  */

public class XMLUtils {

     private final static DocumentBuilder createDocumentBuilder(){

         DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();

         DocumentBuilder builder= null ;

         try {

             factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true );

             builder=factory.newDocumentBuilder();

         } catch (ParserConfigurationException e) {

             // TODO Auto-generated catch block

             e.printStackTrace();

         }

         return builder;

     }

     public final static Document parse(InputSource in){

         try {

             return createDocumentBuilder().parse(in);

         } catch (SAXException e) {

             // TODO Auto-generated catch block

             e.printStackTrace();

         } catch (IOException e) {

             // TODO Auto-generated catch block

             e.printStackTrace();

         }

         return null ;

     }

     public final static Document parse(String xml){

         return parse( new InputSource( new StringReader(xml)));

     }

}

这样就完成了客户端对服务端的调用。毕竟是初步入门。对以后的开发还是有所裨益。

一个客户端和服务端底层传输数据的了解

客户端传入参数,执行String result = (String) call.invoke(new Object[] { [zhanglifeng]})后,实际发送给服务端的是一个soap底层协议自动生成的入参报文。

?

1

2

3

4

5

6

7

8

9

< soapenv:Envelope xmlns:soapenv = "http://schemas.xmlsoap.org/soap/envelope/" xmlns:com = "com.serviceTargetName" >

    < soapenv:Header />

    < soapenv:Body >

       < com:sayHello >

          <!--Optional:-->

          < parameterName >zhanglifeng</ parameterName >

       </ com:sayHello >

    </ soapenv:Body >

</ soapenv:Envelope >

服务端接收这个报文后自动解析,并把入参赋值给方法sayHello(String str)的入参str.经过处理数据返回给客户端,也是soap底层自动生成的回参报文

?

1

2

3

4

5

6

7

< soap:Envelope xmlns:soap = "http://schemas.xmlsoap.org/soap/envelope/" >

    < soap:Body >

       < ns2:sayHelloResponse xmlns:ns2 = "com.serviceTargetName" >

          < return >你好,zhanglifeng  你已成功访问了webservice服务端!</ return >

       </ ns2:sayHelloResponse >

    </ soap:Body >

</ soap:Envelope >

客户端自动把从返回报文中取出返回值,并付值给String result。这样底层已经处理过了报文。还有一种情况就是如果客户端传过来的参数本身就是xml时,底层封装参数的问题。

如果客户端的invoke()方法入参

?

1

2

String xml="

<? xml version=\"1.0\" encoding=\"UTF-8\"?>< userBean >< userName >zhanglifeng</ userName ></ userBean >"

底层自动为xml加上<![CDATA[ xml ]]>:

?

1

2

3

4

5

6

7

8

9

< soapenv:Envelope xmlns:soapenv = "http://schemas.xmlsoap.org/soap/envelope/" xmlns:com = "com.serviceTargetName" >

    < soapenv:Header />

    < soapenv:Body >

       < com:sayHello >

          <!--Optional:-->

          < parameterName > <![CDATA[<?xml version="1.0" encoding="UTF-8"?><userBean><userName>zhanglifeng</userName></userBean> ]]> </ parameterName >

       </ com:sayHello >

    </ soapenv:Body >

</ soapenv:Envelope >

知道这个原理,在使用SoupUI进行调用webservice接口时就可以使用这种<![CDATA[ 入参xml ]]>格式进行调用。soupui调用的入参如下图

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。

原文链接:https://blog.csdn.net/zhanglf02/article/details/73951581

查看更多关于详谈cxf和axis两种框架下的webservice客户端开发的详细内容...

  阅读:22次