一、 声明
写这篇文章完全得益于梁爱虎的《精通SOA基于服务总线的整合应用开发》和一些关于axis开发web service的文章,对于什么是web service,我建议大家去看看EJB的东西,对web service有很好的理解,言归正传,下面开始实际的开发。
二、 环境配置
在这里,我不得不谈环境的配置,因为Java本身是一个开源的架构,这导致各个厂商各自开发自己的东西,升级也不一致,笔者写这篇文章之前是想用Weblogic8.1 作为应用服务器的,但终究因版本的问题和Weblogic本身的复杂性而导致很多例子无法在其上运行,所以最终还是放弃了Weblogic的使用,版本不一致很容易就莫名奇怪的运行不起来,笔者只能保证下面的配置绝对没有问题,这里我采用的版本是
1.JDK1.5.0.6
2.Tomcat5.5(好像5.0及以前的版本运行WebService有点问题)
3.Axis1.4
4.Eclipse3.1.2+MyEclipse 4.1.1GA_E3.1
变量设置:
变量设置是为了当你不用IDE例如Eclipse,你只是简单的通过JDK+Tomcat来完成你的工作时,需要Tomcat解析你的程序(你或者可以不通过变量设置而是直接将需要引用的类放在%TOMCAT_HOME%\common\lib下),当然如果你采用IDE来运行你的程序是,这一步是没有必要的,因为你可以同过添加类的方法来完成这一功能。下面是我的环境配置:
1.AXIS_HOME
D:\Java\axis-1_4(这是我的Axis路径)
2.AXIS_LIB
%AXIS_HOME%\lib
3.AXIS_CLASSPATH
%AXIS_LIB%\axis.jar;%AXIS_LIB%\commons-discovery-0.2.jar;%AXIS_LIB%\commons-logging-1.0.4.jar;%AXIS_LIB%\jaxrpc.jar;%AXIS_LIB%\saaj.jar;%AXIS_LIB%\log4j-1.2.8.jar;%AXIS_LIB%\xml-apis.jar;%AXIS_LIB%\xercesImpl.jar;%AXIS_LIB%\wsdl4j-1.5.1.jar;%AXIS_LIB%\activation.jar;%AXIS_LIB%\xmlrpc-2.0.jar
4.CLASSPATH
.;%JAVA_HOME%\lib\tools.jar;%JAVA_HOME%\lib\dt.jar; %AXIS_CLASSPATH%;
5.在你的%TOMCAT_HOME%\common\lib下需要加入三个包 activation.jar、mail.jar、tools.jar,注意这三个包是必须的,尽管tools.jar很常见,但这也是运行Axis所必须的包。
三、 Axis三种开发方式
Axis(Apache extensible Interaction system)是Apache项目组织的一个开源项目。前身是Apache SOAP,它通过如下方法来扩展了soap2.0的功能:
AXIS的关键功能和优势表现在速度(早期的SOAP的分析机制是基于DOM的,而AXIS是基于SAX的),灵活性(提供了在引擎中插入新扩展的功能,可以对头部信息的处理和系统管理进行定制,在WSDD中对服务,Handler对象和串行并行程序进行描述),面向组件展开(引入了链接chainable和 Handler的概念),传输框架(SOAP可以建立在SMTP, FTP, HTTP等多种传输层协议上)。
Axis支持三种web service的部署和开发,分别为:
1、Dynamic Invocation Interface ( DII)
2、Dynamic Proxy方式
3、Stubs方式
对于前两种 Web Service的发布基本一样,客户端的访问也很类似,第一种发布就是直接将.java后缀改为.jws,并将生成的.class文件拷贝到WEB- INF/jwsclasses下面,这样的例子直接在Axis上就有,非常简单,但它也有缺陷,就是不适合程序部署和大型项目开发,而且不支持包(package)的形式(这个只是我的个人见解),第三种是目前比较流行的方式,stub意思是树桩,意味着服务端和客户端都是通过桩的形式来完成访问的,即在服务端将java转换成wsdl,在客户端将wsdl装换成java,这样就实现了良好的桩的分离,笔者会一一介绍。我在梁先生那部著作中只看到第一种和第三种的介绍,梁先生陈之为基于JavaBean的Web Service开发和基于无状态会话Bean的Web Service开发,后者主要是EJB的内容,可见Stubs的具体功用。
四、 Dynamic Invocation Interface ( DII)开发方式
1. 在Eclipse里新建一个Tomcat Project取名为AxisTest,在接下来的工程中我们也统一用这一工程来演示。
2. 接着是部署Axis所必须的步骤
将axis-1_4\webapps\axis中几个必须的文件拷贝到你的AxisTest工程文件下,一个是WEB-INF\lib下的所有文件,一个是WEB-INF下面的web.xml文件,我们来简单看一下web.xml这个文件
<servlet>
<servlet-name>AxisServlet</servlet-name>
<display-name>Apache-Axis Servlet</display-name>
<servlet-class>
org.apache.axis.transport.http.AxisServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>AxisServlet</servlet-name>
<url-pattern>/servlet/AxisServlet</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>AxisServlet</servlet-name>
<url-pattern>*.jws</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>AxisServlet</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
|
就是说如果向容器请求/servlet/AxisServlet、*.jws、/services/*这几种映射会引起调用AxisServlet这个类,我们要用的就这几行,其他的可以直接删掉。
3. 引用Axis包,由于我们添加了环境变量,所以用DII方法引用Axis包这一步可以省略掉,不过为了以后我们的其他方法引用,我们还是将这些包引用一下。
4. 整个环境部署如下图
图一
5. 在WEB-INF/src下面新建一个myService.java文件,如下
public class myService {
public String getusername(String name){
return "Hello "+name+",this is an Axis DII Web Service";
}
}
|
注意这个java文件是不属于任何package的,如果你要将其放入某个package那么Axis会提示你找不到它生成的class文件从而不能将其转换为wsdl文件。
6. 将myService.java拷贝到AxisTest根目录下,将其后缀改为jws。
7. 启动Tomcat输入http://localhost:8080/AxisTest/myService.jws,会看到如下界面:
图二
8. 点击Click to see the WSDL,如果看到如下界面就表示你已经成功发布一个Web Service了,就这么简单,如果出现错误就表示你的配置错误,或者是你的web.xml有问题,或者是你的包引用有问题,多试几遍这个问题很容易搞定的,这个一定要正确,不然接下来的客户端就访问不到了,这时候你会在WEB-INF下面看到Axis会自动生成一个jwsClasses文件夹,jwsClasses下面会有一个myService.class文件。
图三
Wsdl的具体含义就不用我解释了吧,在我的博客里都有详细的介绍。
9. 接下来是编写客户端来访问这个getService,在com.axistest包下新建一个myServiceTestorByjws.java,选中public static void main(),如下图
图四
代码如下:
public static void main(String[] args) throws ServiceException, MalformedURLException, RemoteException {
String endpoint="http://localhost:8080/AxisTest/myService.jws";
String name=" 邹萍";
Service service = new Service();
Call call = (Call) service.createCall();
call.setTargetEndpointAddress(new java.net.URL(endpoint));
call.addParameter("param1",XMLType.XSD_STRING,ParameterMode.IN);
call.setOperationName( "getusername" );
call.setReturnType( XMLType.XSD_STRING );
String ret = (String) call.invoke( new Object[] { name } );
System.out.println("返回结果:" + ret);
}
|
10. 运行结果如下:
图五
注意第一行,Axis告诉我们缺少activation,mail类,是因为笔者没有引用这两个类,添加引用即可。
五、 Dynamic Proxy方式,WSDD方式
动态代理就是通过wsdd来描述Web服务,而不是直接访问jws,前面说过jws是不支持包的,而且运行也不稳定,有时候可以访问有时候就不可以,所以笔者不推荐此种方法,wsdd和wsdl的区别在于前者只描述Web服务和操作方法以及传输方式,它相对于wsdl要简单的多,更易被人读懂。为此我们需要作如下工作:
1. 为了更好的区分,我们将myService放入com.service包下,这就是WSDD的好处,它不像DII不能建包,并修改代码如下:
package com.service;
public class myService {
public String getusername(String name){
return "Hello "+name+",this is an Axis Web Service";
}
}
|
2. 在WEB-INF下新建一个server-config.wsdd文件,代码如下:
<deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
<handler type="java:org.apache.axis.handlers.http.URLMapper" name="URLMapper"/>
<service name="myService" provider="java:RPC">
<parameter name="className" value="com.service.myService"/>
<parameter name="allowedMethods" value="getusername"/>
</service>
<transport name="http">
<requestFlow>
<handler type="URLMapper"/>
</requestFlow>
</transport>
</deployment>
|
这就是WSDD的全部内容,它描述一个Web Service发布。
3. 测试发布的myService,重启Tomcat,在浏览器输入
图六
4. 接下来写客户端代码,在com.axistest下新建一个
myServiceTestorByWSDD.java,代码如下:
package com.axistest;
import java.net.MalformedURLException;
import java.rmi.RemoteException;
import javax.xml.rpc.ServiceException;
import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
public class myServiceTestorByWSDD {
public tatic void main(String[] args) throws ServiceException,MalformedURLException, RemoteException {
String endpoint = "http://localhost:8080/AxisTest/services/myService";
Service service = new Service(); // 创建一个Service实例,注意是必须的!
Call call = (Call) service.createCall(); // 创建Call实例,也是必须的!
call.setTargetEndpointAddress(new java.net.URL(endpoint));// 为Call设置服务的位置
call.setOperationName("getusername"); // 注意方法名与JavaBeanWS.java中一样!!
String res = (String) call.invoke(new Object[] { "邹萍" }); // 返回String,传入参数
System.out.println(res);
}
}
|
注意上述方法和jws方式唯一的不同就在于endpoint的引用方式不同,一个是直接引用jws文件,一个是引用一个wsdd描述的服务。
5. 运行结果如图:
图七
六、 Stubs方式
这种实现方式是目前比较流行的方式,他将用于发布服务的提供商和用于引用服务的应用商有效的实现了分离,而且较之于前两种开发方法,Stubs不需要程序员关注WebService的返回类型,也就是不用我们去关心wsdl,因为他是axis自动生成的,例如webservice要返回一个Stirng[]类型,axis自动将其转化为Array,调用也简单。这种方式是在EJB基础上发展起来的,熟悉EJB的人应该很熟悉这种方式,所以stubs方式是这几种开发方式中最成熟的方法,下面是步骤:
1. 在AxisTest根目录下建立一个java2wsdl文件夹,在java2wsdl文件夹下新建一个名为build.xml的Ant构建,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<project name="Generate WSDL from JavaBeans as Web Services" default="j2w-all" basedir=".">
<property name="build.dir" value="../WEB-INF/classes"/>
<property name="axis.dir" location="D:/Java/axis-1_4"/>
<path id="classpath.id">
<fileset dir="${axis.dir}/lib">
<include name="*.jar"/>
</fileset>
<pathelement location="${build.dir}"/>
</path>
<taskdef name="axis-java2wsdl" classname="org.apache.axis.tools.ant.wsdl.Java2WsdlAntTask"
loaderref="axis" >
<classpath refid="classpath.id"/>
</taskdef>
<target name="j2w-all">
<antcall target="j2w-JavaBeanWS"/>
</target>
<target name="j2w-JavaBeanWS">
<axis-java2wsdl classname="com.service.myService" classpath="${build.dir}"
methods="getusername"
output="myService.wsdl"
location="http://localhost:8080/AxisTest/services/myService"
namespace="http://localhost:8080/AxisTest/services/myService"
namespaceImpl="http://localhost:8080/AxisTest/services/myService">
</axis-java2wsdl>
</target>
</project>
|
关于Ant构建的语法请参考相关文章,简单说一下一个是axis.dir,就是你下载的Axis-1.4的目录,一个是<target name="j2w-JavaBeanWS">,classname表示你的myService存放的路径,methods表示webservice中的方法,方法名称必须和myService中的方法一致,否则即使构建成功了也不能执行,output是输出的wsdl名称,location是myService的访问地址,用于客户调用,namespace是命名空间,有了命名空间你就可以通过namespace.webservice的方法来调用这个webservice,好了,关于Ant构建就介绍到这里。
2. 运行Ant构建,在java2wsdl目录下会自动生成一个myService.wsdl的文件,运行结果如下
图八
3. 下面开始客户端的工作,客户端就是把服务端的wsdl文件转换为java文件以便于客户端的调用执行,理论上服务端和客户端是分离的,他们属于不同的系统和项目,两者毫无相干,唯一联系他们的是wsdl,但我们为了方便都放在AxisTest项目中,客户端的做法是将发布商发布的wsdl文件拷贝到自己的项目中,在这里我们已经有了这个wsdl就可以省去这一步,在AxisTest新建一个源文件夹(Source Folder),注意是源文件夹不是文件夹,取名为wsdl2java,新建一个build.xml的Ant构建,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<project name="wsclient" default="all" basedir=".">
<property name="axis.home" location="D:\Java\axis-1_4"/>
<property name="options.output" location="../wsdl2java"/>
<path id="axis.classpath">
<fileset dir="${axis.home}/lib">
<include name="**/*.jar"/>
</fileset>
</path>
<taskdef resource="axis-tasks.properties" classpathref="axis.classpath" />
<target name="-WSDL2Axis" depends="init">
<mkdir dir="${options.output}"/>
<axis-wsdl2java output="${options.output}" url="${options.WSDL-URI}" verbose="true"/>
</target>
<target name="init">
<echo>Warning: please update the associated WSDL file(s) in the folder wsdl before running the target!</echo>
<echo>Warning: Just run the target(s) related with your developing work!</echo>
<echo></echo>
</target>
<target name="all">
<antcall target="myService"/>
</target>
<target name="myService">
<antcall target="-WSDL2Axis">
<param name="options.WSDL-URI" location="../java2wsdl/myService.wsdl"/>
</antcall>
</target>
</project>
|
4. 运行Ant构建,运行结果如下:
图九
这是在wsdl2java文件夹会自动生成几各类,至此,Stubs的整个结构目录如下:
图十
别看Axis自动生成这么多类,其实都很简单,我们真正实际用到的就*ServiceLocator.java和*.java类,ServiceLocator是获得webservice的定位,*.java是具体实现类。
5. 编写客户端测试代码,在com.axistest新建一个myServiceTestorByStubs.java,内容如下:
package com.axistest;
import localhost.AxisTest.services.myService.MyService;
import localhost.AxisTest.services.myService.MyServiceServiceLocator;
public class myServiceTestorByStubs {
public static void main(String[] args) throws Exception
{
MyServiceServiceLocator Service= new MyServiceServiceLocator();
MyService port= Service.getmyService();
String response=port.getusername("邹萍");
System.out.println(response);
}
}
|