一 ,环境配置
在这里,我不得不谈环境的配置,因为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来运行你的程序是,这一步是没有必要的,因为你可以同过添加类的方法来完成这一功能。下面是我的环境配置:
变量设置是为了当你不用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);
}
}