1 :WebService框架WebService-CXF
1.1 简介:
1.1.1 什么是CXF
l Apache CXF = Celtix + Xfire :结合Celtix + Xfire的优点;
l 支持多种协议:
• SOAP1.1,1,2
• XML/HTTP
• CORBA(Common Object Request Broker Architecture公共对象请求代理体系结构,早期语言使用的WS。C,c++,C#)
• 并可以与Spring进行快速无缝的整合
• 灵活的部署:可以运行在Tomcat,Jboss,Jetty(内置),IBMWS,BeaWL上面。
1.1.2 CXF的结构:
|
1.1.2.1 Bus CXF框架的支撑
Bus是CXF框架的支撑.CXF Bus由spring配置文件构成(cxf.xml),在servlet初始化时
通过SpringBusFactory加载,它为所有终端定义了公共的上下文.它织入了所有运行
时的结构组件并提供了一个公共的应用上下文.SpringBusFactory扫描并加载类路径
下的META-INF/cxf目录并从以下文件构建上下文.
META-INF/cxf/cxf.xml
META-INF/cxf/cxf-extension.xml
META-INF/cxf/cxf-property-editors.xml
XML文件是安装CXF类库的一部分.CXF内部使用Spring配置.cxf.xml文件中bus定义如
下:
<bean id="cxf"class="org.apache.cxf.bus.CXFBusImpl" />
核心bus组件是CXFBusImpl,该类更多扮演拦截器供应商的角色对于终端出入的请求.
这些拦截器一旦应用,即可用于上下文中所有的终端.cxf.xml文件也定义了其他的组
件,比如BindingFactoryManager, ConduitFactoryManager等等.这些组件可用于bus
的扩展.可以使用getExtension()方法这些组件.注册这些组件以用于获取或更新服
务终端级别参数如服务绑定,传输协议,中转等等.
--------------
1.1.2.2Frontend-前端:
Frontend
CXF提供了前端建模的概念,可以使用不同的前端API创建ws.这些API使用简单工厂
bean和JAX-WS实现创建ws.可以创建动态ws客户端.CXF支持的前端主要是JAX-WS.
---------
1.1.2.3Message and intercetpors: 消息和拦截器
CXF框架中重要的组件之一是拦截器组件.拦截器拦截客户端和服务器间的消息.在
CXF,这通过拦截器链的概念实现.拦截器链是CXF运行时的核心功能.链中的每个拦截
器是可配的,用户可以控制他的执行.
框架的核心的是拦截器接口,它定义了两个方法handleMessage和handleFault,都携
带Message类型作为参数.
拦截器通常分组成各阶段.每个阶段执行特定的消息处理,每个阶段又被添加到拦截
器链中,因此,链有时有序的拦截器阶段的列表.典型的ws终端有三个连接器链:
Inbound messages chain(入消息链)
Outbound messages chain(出消息链)
Error messages chain(错误消息链)
内置拦截器如日志,安全,也可以自定义拦截器.
-----
1.1.2.4Service Model-主要用于生成wsdl文件的各个元素。
建模service,创建各种WSDL元素如操作,绑定,终端,schema等等.
如下显示了服务模型中的各个组件:
-------
1.1.2.5 Data binding
数据绑定是ws开发的关键.它意味着java对象和xml元素之间的映射.数据绑定组件执
行这一工作.CXF最新版本使用JAXB2.1.JAXB使用注解定义java对象和XML之间的映射.如下:
@XmlRootElement(name="processOrder",
namespace="http://localhost/orderprocess")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name="processOrder",namespace="http://localhost/orderprocess")
public class OrderProcess {
@XmlElement(name="arg0",namespace="")
privateorder.Order arg0;
//Gettterand Setter
…
}
-----------
协议绑定:protocol bindings
CXF支持以下形式的绑定:
SOAP1.1
SOAP1.2
CORBA
Pure XML
--
数据传输——transport
CXF支持以下数据传输方式:
HTTP
CORBA
JMS
LOCAL
下载完成后将其解压到某个目录下
1.2 Ant +CXF+java环境搭建
l Ant
l Tomcat
l 并设置以下环境变量:
• JAVA_HOME
• CXF_HOME
• ANT_HOME
• CATALINA_HOME (tomcat_hone)
• Path =%JAVA_HOME%\bin;%CXF_HOME%\bin;%CATALINA_HOME%\bin;%ANT_HOME%\bin
• CLASSPATH=.;%CXF_HOME%\lib\cxf-manifest.jar;.\build\classes
1.2.1 1、为什么要用到ant这个工具呢?
Ant做为一种工具已经广泛被使用,并且历史悠久。使用ant的内置命令,可以编译java源文件(javac),运行java文件(java),给class文件打包(jar、war、ear),也可以创建(mkdir)、删除(del)、拷贝(copy),甚至可以使用ant执行sql文件。由于ant是用xml语言写成的文件,并取默认名为build.xml文件。所以,今后大家应该在见到名为build.xml文件时知道这是一个ant的文件。
2、默认在Eclipse和MyEclipse的对ant有内建的支持。大家可以在MyEclipse中任意项目中建立一个文件叫build.xml文件,你会发现它有不同的图标(上面有个小蚂蚁)。
1.2.2 用ANT来构造第一个CXF示例:
l 在cxf_home\samples\java_first_pojo目录下启动服务器:
• 在命令行模式下进入上述目录:
• 执行 以下命令 ant server (server不是固定的_这里使用server)
l 在同一目录下打开新命令行窗口运行:
• ant client
l 上面的代码,其实就是通过ant工具对源代码进行编译和运行。
l 如果能编译通过,将会显示buildsuccess.
•
1.2.3 用ANT来构造第一个CXF示例:
查看它的源代码并修改运行:
1.2.3.1用ANT将它发布到Tomcat服务器上:
l 1、执行ant clean清除以前生成的代码
l 2、执行ant war打包此应用。
l 3、执行ant deploy –Dtomcat=true将项目发布到tomcat的服务器上。
l 4、启动tomcat服务器
l 5、打开IE的地址栏输入:
http://localhost:9999/helloworld/services/hello_world?wsdl
或:http://localhost:9999/helloworld/services
l 6、在命令行执行:
• ant client-servlet –Dbase.url=http://localhost:9999
• 或在MyEclipse的WebServiceExplorer中访问这个WebService
l 7、停止tomcat服务器。
l 8、卸载应用:ant undeploy –Dtomcat=true
l 9、清空项目:ant clean
1.3 Eclipse中开发CXF的JavaSE应用:
l 导入CXF中的 所有jar文件。可以通过WACH_JARS来观察各jar文件的用途。
l 建立好之后的项目如
右图所示:
CXF发布服务的类:
l 用两个不同的类发布应用:
l ServerFactoryBean --FacotryBean
l JaxWsServerFactoryBean(建议使用此类)
1.3.1 使用ServerFactoryBean发布服务:(不建议使用)
l 使用ServerFactoryBean发布服务。
l 使用CXF发布一个服务,与JDK6发布一个服务完全不同
• * 即使是不使用@WebService注解,一样可以发布成功
• * 即使此类没有对外公布的方法一样可以发布成功
package cn.itcast.cxf1; import java.util.Date; import org.apache.cxf.frontend.ServerFactoryBean; /** * 使用ServerFactoryBean发布服务 * 使用CXF发布一个服务,与JDK6发布一个服务完全不同 * 即使是不使用@WebService注解,一样可以发布成功 * 即使此类没有对外公布的方法一样可以发布成功 */ public class CxfOneServer { public String sayHi(){ return "Hello:"+new Date(); } public static void main(String[] args) { ServerFactoryBean bean = //声明实例 new ServerFactoryBean(); //绑定到发布地址的端口 bean.setAddress("http://127.0.0.1:9999/one"); //设置服务接口,如果没有接口,则为本类 bean.setServiceClass(CxfOneServer.class); //设置接口实现类,即服务类 bean.setServiceBean(new CxfOneServer()); bean.create();//发布服务 System.err.println("启动成功"); } }
|
1.3.2 使用JaxWsServerFactoryBean发布服务:(建议使用此类)
l JaxWsServerFactoryBean是ServerFactoryBean的子类,也是功能扩展类。
l 但在CXF的API文档中没有提供此类API,请通过查看源代码的方式获取此类的帮助。
l 此类,必须要在被发布为服务的类上添加@WebService注解,如果不加注解,虽然不
出错,但也不会对外暴露任何方法。
使用此类生成的wsdl文件更加规范。
l 以下是从它的源代码中找到的对此类的说明。
package cn.leaf.one; import javax.jws.WebService; import org.apache.cxf.jaxws.JaxWsServerFactoryBean; @WebService public class OneService { public String sayHi(){ return "Good"; } public static void main(String[] args) throws Exception { JaxWsServerFactoryBean bean //使用jaxWs对其进行发布 = new JaxWsServerFactoryBean(); bean.setServiceBean(new OneService()); bean.setServiceClass(OneService.class); bean.setAddress("http://localhost:4444/one"); bean.create(); System.err.println("服务启动成功。。"); //Thread.sleep(1000*60*60); //System.exit(0); } }
|
1.3.3 使用ClientProxyFactoryBean客户端调用
在同一个项目中调用
package cn.itcast.cxf1; import org.apache.cxf.frontend.ClientProxyFactoryBean; public class Client { public static void main(String[] args) { ClientProxyFactoryBean bean = //创建客户端类 new ClientProxyFactoryBean(); //设置访问地址 bean.setAddress("http://localhost:9999/one"); //设置服务接口,直接使用本项目中的接口 bean.setServiceClass(CxfOne.class); //通过create方法返回接口实例 CxfOne s = (CxfOne) bean.create(); String str = s.sayHi();//调用 System.err.println(str); } }
|
在不同的项目中调用
public static void main(String[] args) { ClientProxyFactoryBean bean = //创建客户端类 new ClientProxyFactoryBean(); //设置访问地址 bean.setAddress("http://localhost:9999/one"); //设置服务接口,直接使用本项目中的接口 bean.setServiceClass(CxfOnePortType.class); //通过create方法返回接口实例 CxfOnePortType one = (CxfOnePortType) bean.create(); String s = s.sayHi();//调用 System.err.println(s); }
|
1.4 CXF的实践
1.4.1 使用CXF的类发布一个服务:
packagecn.leaf.one;
import javax.jws.WebService;
import org.apache.cxf.jaxws.JaxWsServerFactoryBean;
@WebService
publicclassOneService {
publicString sayHi(){
return"Good";
}
publicstaticvoidmain(String[] args)throwsException {
JaxWsServerFactoryBeanbean//使用jaxWs对其进行发布
= new JaxWsServerFactoryBean();
bean.setServiceBean(new OneService());
bean.setServiceClass(OneService.class);
bean.setAddress("http://localhost:4444/one");
bean.create();
System.err.println("服务启动成功。。");
//Thread.sleep(1000*60*60);
//System.exit(0);
}
}
1.4.2 使用JaxWsProxyFactoryBean客户端调用
packagecn.itcast.cxf2;
importorg.apache.cxf.jaxws.JaxWsProxyFactoryBean;
publicclassA {
publicstaticvoid main(String[] args) {
JaxWsProxyFactoryBean bean =
newJaxWsProxyFactoryBean();
bean.setAddress("http://localhost:9999/two");
//此外注册的必须是一个接口,否则将抛出异常信息
bean.setServiceClass(Cxf2Server.class);
Cxf2Server s = (Cxf2Server) bean.create();
System.err.println(s.sayHello());
}
}
1、在使用CXF发布服务时,需要首先写一个接口,用以声明服务类型。这有点类似于Spring中的服务层的实现。
package cn.test;
import javax.jws.WebService;
import javax.xml.ws.BindingType;
import javax.xml.ws.soap.SOAPBinding;
@WebService
@BindingType(value=SOAPBinding.SOAP12HTTP_BINDING)
publicinterface IHelloService {
public String sayHello(String name);
}
1.4.2.1wsdl2java生成客户代码
l 在cxf中,也提供了一个用于生成客户端调用代码的工具。它的功能就如同wsimport一样。
l 先让我们了解一下cxf的wsdl2java工具,可以生成一堆客户端调用的代码。
l 此工具位于cxf_home/bin目录下。参数与wsimport有所不同。
l 它包含以下参数:
• -d参数,指定代码生成的目录。
• -p参数,指定生成的新的包结构。
l 需要说明的是,由于wsdl2java是根据jdk1.7生成的本地代码,所以,需要对生成的代码做一点点修改。
l 在命令行执行:
wsdl2java–d . http://127.0.0.1:6666/helloworld?wsdl
1、注意:由于使用的是apache-cxf-2.4.0版本,它是支持jdk1.7的。所以,对于生成的Service要进行稍微的修改。
在jdk1.6中的javax.xml.ws.Service的构造方法接收二个参数为:(String url,QName qname);
在jdk1.7中的javax.xml.ws.Service的构造方法中接收三个参数为:(String url,QName qname,features);
2、将生成的代码,拷贝到项目目录,然后使用以前相同方法调用。
注意:如果你对@WebMethod设置了header=true参数,将会在调用时多传递一个参数。它参数可以直接传null值。
对于这种情况,可以将header=true修改成header=false然后再重要获取客户端源代码。
3、大家可能发现了,使用cxf生成的客户端代码与wsimport差不多,甚至是一样,那为什么还要使用cxf呢?
它较好的发布方式、较容易的与其他服务器集成、及与Spring的完美结合都不得不让我们使用cxf。
调用端代码:
1、必须已经使用wsimport或wsdl2java生成了客户端代码。 以下是源代码: packagecom.run; importcom.itcast.cxfserver.javaclient.IHelloWorld; importcom.itcast.cxfserver.javaclient.IHelloWorldPortType; /** * 使用java6来调用cxf生成的客户端类的服务 * 不管是用wsimport还是用wsdl2java生成的类基本上是完全一样的 */ publicclassCxfServerJava6Client { publicstaticvoid main(String[] args) { //调用使用wsimport生成的代码 IHelloWorldPortType hello = newIHelloWorld().getIHelloWorldPort(); String str = hello.sayHello("小王"); System.err.println(str);
//调用使用wsdl2java生成的代码(一样) com.itcast.cxfserver2.javaclient.IHelloWorldPortType client = newcom.itcast.cxfserver2.javaclient.IHelloWorld().getIHelloWorldPort(); String str2 = client.sayHello("CXF"); System.err.println(str2); } }
3、要想研究更多程序请见:cxf2.4_ws服务项目和cxf2.4_java6client客户端项目。
|
1.4.3 给服务添加消息拦截器:-这个非常有用:
在服务发布之前添加拦截器:
l LoggingInInterceptor – 信息输入时的拦截器 –请求
l LoggingOutInterceptor –信息输出时的拦截器-响应
server.getInInterceptors().add(new LoggingInInterceptor());
server.getOutInterceptors().add(new LoggingOutInterceptor());
给服务器添加拦截器,类似于使用TCP/IP Monitor来捕获SOAP消息的过程。
package cn.itcast.cxf;
import org.apache.cxf.interceptor.LoggingInInterceptor; import org.apache.cxf.interceptor.LoggingOutInterceptor; import org.apache.cxf.jaxws.JaxWsServerFactoryBean;
/** * * 使用JaxWsServerFactoryBean发布CXF的Web服务 * 必须加入WebService注解,如果不加,虽然不报错,但是所有的方法都暴露不出来 */
public class Server {
public static void main(String[] args) { JaxWsServerFactoryBean bean = new JaxWsServerFactoryBean(); bean.setAddress("http://192.168.1.100:7788/hello"); //提供服务的类的类型或接口类型 bean.setServiceClass(IHelloService.class); bean.setServiceBean(new HelloServiceImpl()); //可以多次针对不同的对象在(srping)中配置 //加入请求的消息拦截器 bean.getInInterceptors().add(new LoggingInInterceptor()); //加入请求的消息拦截器 bean.getOutInterceptors().add(new LoggingOutInterceptor());
bean.create(); System.out.println("Hello Service ready..."); } }
|
1.4.4 使用JavaScript也可以访问WebServie:
l jQuery访问WebService.
• 1、在本域准备jquery.js (由于jquery对跨域的请求做了限制,所以jquery仅限于在本域中使用。但为了快速解析XML文件,仍然可以使用jquery当成解析xml文件的工具。)。
• 2、通过js发起ajax请求。
<%@pagelanguage="java"import="java.util.*"pageEncoding="UTF-8"%> <%@ tagliburi="http://java.sun.com/jsp/jstl/core" prefix="c" %> <html> <head> <script type="text/javascript" src="<c:url value='/js/jquery-1.6.2.js'/>"></script> </head> <body> <p>用js来控制的客户端</p> <button id="all">显示所有</button><br/> <hr/> Name:<input type="text" id="_name">,Age:<input type="text" id="_age"> <button id="add">增加一个</button><br/> <table id="tb" width="70%" border="1"> <tr> <th>学生姓名</th> <th>学生年龄</th> </tr> </table> </body> <script type="text/javascript"> $(function(){ var url = "http://localhost:7777/xcxf3_ss/ws/studQuery"; $("#all").click(function(){ //查询时必须向服务器发以下xml文件 var xml = '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">'+ '<soap:Body><ns2:query xmlns:ns2="http://service.stud.cn/"/>'+ '</soap:Body></soap:Envelope>'; $.ajax({ url:url, type:'post', dataType:'xml', contentType:'application/soap+xml;charset=UTF-8', data:xml, success:function(data){ $("#tb tr:gt(0)").remove();//删除所有列 //遍历data $(data).find("return").each(function(){ var name = $(this).find("name").text(); var age = $(this).find("age").text(); var tr = $('<tr><td>'+name+'</td><td>'+age+'</td></tr>'); tr.appendTo("#tb"); }); } }); //以下是增加 $("#add").click(function(){ var nm = $("#_name").val(); var age = $("#_age").val(); if($.trim(nm)=="" || $.trim(age)==""){ alert('姓名和年龄必须输入'); } //以下是保存服务的xml var xml = '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">' +'<soap:Body><ns2:add xmlns:ns2="http://service.stud.cn/">' +'<arg0><age>'+age+'</age><name>'+nm+'</name></arg0></ns2:add>' +'</soap:Body></soap:Envelope>'; $.ajax({ url:url, type:'post', dataType:'xml', contentType:'application/soap+xml;charset=UTF-8', data:xml, success:function(){ alert('保存成功'); } }); }); }); }); </script> </html>
|
l 如何通过ant发布第一个cxf应用。
l 手工建立一个java项目发布cxf的ws.
l Java项目和js访问webService
l 加入消息拦截器
l 将服务端改为SOAP1.2实现
l SOAP1.1与1.2