WebService-CXF
来源:互联网 发布:笑果软件融合 编辑:程序博客网 时间:2024/05/18 01:10
WebService-CXF
注:如果只想开发一个简单的WebService,蓝色标注的文字可以不看。
红色字体是需要注意的地方。
简介:Apache CXF == Celtix + Xfire, CXF 继承了 Celtix 和 XFire 两大开源项目的精华,提供了对JAX-WS 全面的支持. CXF 既支持 WSDL 优先开发,也支持从 Java 的代码优先开发模式。容易使用: CXF 设计得更加直观与容易使用。有大量简单的 API 用来快速地构建代码优先的 Services,各种 Maven 的插件也使集成更加容易,支持 JAX-WS API ,支持 Spring 2.0 更加简化的 XML 配置方式,等等。支持二进制和遗留协议:CXF 的设计是一种可插拨的架构,既可以支持 XML ,也可以支持非 XML 的类型绑定,比如:JSON 和 CORBA.
优点:
1 可支持代码生成:
Java to WSDL;WSDL to Java;XSD to WSDL;WSDL to XML;WSDL to SOAP;WSDL to Service;
2 Apache CXF 提供方便的Spring 整合方法,可以通过注解、Spring 标签式配置来暴露WebServices
CXF开发:
CXF适合与Spring一起开发,JAVA 中共有三种WebService 规范,分别是JAXM&SAAJ、JAX-WS(JAX-RPC)、JAX-RS。这里的JAX-WS 和JAX-RS 规范我们采用Apache CXF 作为实现,CXF 是Objectweb Celtix和Codehaus XFire 合并而成。CXF 的核心是org.apache.cxf.Bus(总线),类似于Spring 的
ApplicationContext,Bus 由BusFactory 创建,默认是SpringBusFactory 类,可见默认CXF是依赖于Spring 的,Bus 都有一个ID,默认的BUS 的ID 是cxf。
在这里简单说下注意事项:当你使用的是JDK1.5 的时候你就必须要有jaxws-api-2.0.jar这个包的支持.如果使用的是JDK1.6 就不用使用这个包了。因为1.6 里已经有了相关的实现。
CXF与JDK1.6版本开发时代码冲突的解决方法:
如果是java应用程序:在<java-home>/lib/endorsed中将jaxb-api.jar和jaxb-impl.jar放入这里。
如果是java web项目:在<tomcat安装目录>/common/endorsed中将jaxb-api.jar和jaxb-impl.jar放入这里。
注:具体的目录可以通过System.out.println(System.getProperty("java.endorsed.dirs"));
这句代码查看。
Java-Webservice-JAX Ws的开发:
分为Service和Client两部分,Server 公开Web 服务,Client 调用Web 服务,JAX-WS 的服务端、客户端双方传输数据使用的SOAP 消息格式封装数据。
服务端示例:
1 需要一个Web服务的接口:例如IhelloService:
@WebService
public interface IHelloService {
Customer selectMaxAgeStudent(Customer c1, Customer c2);
Customer selectMaxLongNameStudent(Customer c1, Customer c2);
}
注解@WebService 就标注了这个接口的方法将公开为Web 服务,使用了这个注解的接口的所有方法都将公开为Web 服务的操作,如果你想屏蔽某个方法,可以使用方法注解@Method 的exclude=true.
我们也通常把公开为Web服务的接口叫做SEI(Service EndPoint Interface)服务端点接口.
2 接口的实现类:
public class HelloServiceImpl implements IHelloService {
@Override
public Customer selectMaxAgeStudent(Customer c1, Customer c2) {
if (c1.getBirthday().getTime() > c2.getBirthday().getTime())
return c2;
else
return c1;
}
@Override
public Customer selectMaxLongNameStudent(Customer c1, Customer c2)
{
if (c1.getName().length() > c2.getName().length())
return c1;
else
return c2;
}
}
是如果你的实现类还实现了其他的接口,那么你需要在实现类上使@WebService 注解的endpointInterface 属性指定那个接口是SEI(全类名)
3 Customer类:
@XmlRootElement(name = "Customer")
public class Customer {
private long id;
private String name;
private Date birthday;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
@XmlRootElement 将一个Java类映射为一段XML的根节点
参数:name 定义这个根节点的名称
namespace 定义这个根节点命名空间
4 发布Web服务(java应用程序的发布方式)
public class SoapServer {
public static void main(String[] args) {
Endpoint.publish("http://127.0.0.1:8080/helloService",
new HelloServiceImpl());
}
}
注意我们发布Web 服务使用的是javax.xml.ws.*包中的EndPoint 的静态方法publish().
访问http://127.0.0.1:8080/helloService?wsdl , 如果你已经看到WSDL,那么表示我们的Web 服务发布成功了。我没在没有Tomcat这样的Web服务器,而直接运行一个main方法为什么能发布呢?因为CXF内置了Jetty(Servlet容器),因此也可以发布。
常用注解:
@WebService
argetNamespace 属性指定你想要的名称空间(<wsdl:definitions … 中targetNamespace的值)
operationName 属性值指定XXX和XXXResponse 元素中XXX 的值。其中XXX 是方法名称
例如: <wsdl:message name="getClassByJson">
<wsdl:part element="tns:getClassByJson" name="parameters" />
</wsdl:message>
<wsdl:message name="getClassByJsonResponse">
<wsdl:part element="tns:getClassByJsonResponse" name="parameters" />
</wsdl:message>
@ WebResult
Name属性指定,方法的返回值
@WebParam
Name属性指定,参数名
mode 属性 由javax.jws.WebParam.Mode 枚举指定,表示参数的流向,默认是IN,也就是输入参数,还可以是OUT、INOUT 类型。
如果是OUT、INOUT 类型的参数类型,这样的方法参数将会被当做返回值在Web 服务调用完成后返回给你,客户端生成代码时会被转变为javax.xml.ws.Holder<T>类型,注意不要导错包,不是javax.xml.rpc.Holder 类型(JDK1.6 已经没有这个类型,但是MyEclipse 中还是会有,小心导入这个已经不使用的类型)。
@XmlRootElement 将一个Java类映射为一段XML的根节点
参数:name 定义这个根节点的名称
namespace 定义这个根节点命名空间
例如:<xs:element name="Customer" type="tns:customer" />
@XmlAccessorType 定义映射这个类中的何种类型需要映射到XML。可接收四个参数,分别是:
XmlAccessType.FIELD:映射这个类中的所有字段到XML
XmlAccessType.PROPERTY:映射这个类中的属性(get/set方法)到XML
XmlAccessType.PUBLIC_MEMBER:将这个类中的所有public的field或property同时映射到XML(默认)
XmlAccessType.NONE:不映射
@XmlElement 指定一个字段或get/set方法映射到XML的节点。如,当一个类的XmlAccessorType 被标注为PROPERTY时,在某一个没有get/set方法的字段上标注此注解,即可将该字段映射到XML。
参数:defaultValue 指定节点默认值
name 指定节点名称
namespace 指定节点命名空间
required 是否必须(默认为false)
nillable 该字段是否包含 nillable="true" 属性(默认为false)
type 定义该字段或属性的关联类型
@XmlAttribute 指定一个字段或get/set方法映射到XML的属性。
参数:name 指定属性名称
namespace 指定属性命名空间
required 是否必须(默认为false)
@XmlTransient 定义某一字段或属性不需要被映射为XML。如,当一个类的XmlAccessorType 被标注为PROPERTY时,在某一get/set方法的字段上标注此注解,那么该属性则不会被映射。
@XmlType 定义映射的一些相关规则
参数:propOrder 指定映射XML时的节点顺序
factoryClass 指定UnMarshal时生成映射类实例所需的工厂类,默认为这个类本身
factoryMethod 指定工厂类的工厂方法
name 定义XML Schema中type的名称
namespace 指定Schema中的命名空间
@XmlElementWrapper 为数组元素或集合元素定义一个父节点。如,类中有一元素为List items,若不加此注解,该元素将被映射为
<items>...</items>
<items>...</items>
这种形式,此注解可将这个元素进行包装,如:
@XmlElementWrapper(name="items")
@XmlElement(name="item")
public List items;
将会生成这样的XML样式:
<items>
<item>...</item>
<item>...</item>
</items>
@XmlJavaTypeAdapter 自定义某一字段或属性映射到XML的适配器。如,类中包含一个接口,我们可以定义一个适配器(继承自 javax.xml.bind.annotation.adapters.XmlAdapter类),指定这个接口如何映射到XML。
@XmlSchema 配置整个包的namespace,这个注解需放在package-info.java文件中。
jaxb编码:
JAXBContext jaxbContext = JAXBContext.newInstance(clazz);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
InputStreamReader reader=new InputStreamReader(inputStream,"GBK"); //在此修改编码
return unmarshaller.unmarshal(reader);
@javax.jws.Oneway
是一个标识性注解,没有任何属性,它表示公开的Web 服务的方法
没有任何返回值,不允许有 OUT 类型的参数,不允许抛出非运行时异常。如果条件不符JAX-WS 规范要求应该报告错误,但是CXF 的策略是如果方法存在返回值,生成客户端时将被改为void;如果方法参数含有OUT 类型,生成客户端时将被忽略;如果方法含有INOUT类型参数,生成客户端时将只作为IN 类型参数被保留。
分析发布后生成的WSDL的解析:
(1.)<wsdl:definitions …这个是WSDL 的根元素,我们要关心的是三个属性,name 属性值为公开的Web 服务的接
口的实现类+Service Service(上例中为name="HelloServiceImplService");targetNamespace 指定目标名称空间,targetNamespace 的值被后面
的xmlns:tns 属性作为值, 默认是使用接口实现类的包名的反缀
(targetNamespace="http://server.soap.ilkj.net/" …
xmlns:tns="http://server.soap.ilkj.net/"),
你可以使用@WebService 注解的targetNamespace 属性指定你想要的名称空间。
(2.)<wsdl:types …
这个元素会通过<xs:element … 声明几个复杂数据类型的元素。
一般首先你看到的是Web 服务中的方法参数、返回值所涉及的所有复杂(complex)类型的
元素定义<xs:element …,其中name 属性值是这个复杂类型的JAXB 注解的name 属性值,
type 属性是tns:+JAXB 注解的name 属性值的全小写形式(上例中的方法参数、返回值只涉
及一个复杂类型Customer,Customer 的@XmlRootElement 注解的name 属性值为Customer,
因此你会看到<xs:element name="Customer" type="tns:customer" />)。
再向下你会看到XXX 元素和XXXResponse 元素,其中XXX 是方法名称(你可以使用
@WebMethod 的operationName 属性值指定XXX 的值),XXX 是对方法参数的封装,
XXXResponse 是对返回值的封装。上例中你会看到
<xs:element name="selectMaxAgeStudentMethod"
type="tns:selectMaxAgeStudentMethod" />
<xs:element name="selectMaxAgeStudentMethodResponse"
type="tns:selectMaxAgeStudentMethodResponse" />
<xs:element name="selectMaxLongNameStudent"
type="tns:selectMaxLongNameStudent" />
<xs:element name="selectMaxLongNameStudentResponse"
type="tns:selectMaxLongNameStudentResponse" />
最 后你会看到一组<xs:complexType … 元素, 这个元素通过name 属性关联到<xs:element … ,它为前面定义的元素指定封装的具体内容(通过子元素<xs:sequence … 指定),上例中方法参数的复杂类型指定如下形式:
<xs:complexType name="selectMaxAgeStudentMethod">
<xs:sequence>
<xs:element minOccurs="0" name="arg0" type="tns:customer" />
<xs:element minOccurs="0" name="arg1" type="tns:customer" />
</xs:sequence>
</xs:complexType>
我们看到方法参数名称为arg0、arg1、…,如果你想指定方法参数的名字在方法参数前使用
@WebParam 的name 属性指定值,同样,方法的返回值同样可以使用@WebResult 注解指定
相关的属性值。
例如:
@WebResult(name = "method")
Customer selectMaxAgeStudent(@WebParam(name = "c1") Customer c1,
@WebParam(name = "c2") Customer c2);
(3.)<wsdl:message …
这个元素将输入参数(方法参数)和响应结果(方法返回值)、受检查的异常信息包装为消息。
(4.)<wsdl:portType …
这个元素指定Web 服务的端口类型(Web 服务会被发布为EndPoint 端点服务),它的name属性默认为接口名称(你可以使用@WebService 注解的name 属性指定值)。这个元素包含了一系列的<wsdl:operation …子元素指定该端点服务包含了那些操作( 方法) ,
<wsdl:operation …的子元素<wsdl:input …、<wsdl:output …指定操作的输入输出(通过属性message 绑定到前面声明过的消息)。
(5.)<wsdl:binding …
这个元素将前面最终的端点服务绑定到SOAP 协议(你可以看出来WSDL 从上到下依次有着依赖关系),其中的<soap:xxx … 的style、use 分别可以使用SOAPBinding 注解的style、use 属性指定值、<wsdl:operation … 指定公开的操作(方法)。这部分XML 指定最终发布的Web 服务的SOAP 消息封装格式、发布地址等。
(6.)<wsdl:service …
这个元素的name 属性指定服务名称(这里与根元素的name 属性相同),子元素<wsdl:port…的name 属性指定port 名称,子元素<soap:address … 的location 属性指定Web 服务的地址。
利用WSDL生成客户端代码的方法:
你可以在命令行将当前目录切换到CXF 的bin 目录,然后运行wsdl2java –h 查看这个批处理命令的各个参数的作用,常用的方式就是wsdljava –p 包路径 –d 目标文件夹 wsdl 的url地址。现在我们将前面的WSDL生成客户端代码:
wsdl2java -p net.ilkj.soap.client –d E:\ http://127.0.0.1:8080/helloService?wsdl
你会在E 盘根目录找到生成的客户端代码,然后将它复制到Eclipse 工程即可使用。
我们上面的WSDL 会生成如下所示的客户端代码:
Customer.java
HelloServiceImplService.java
IHelloService.java
ObjectFactory.java
package-info.java
SelectMaxAgeStudent.java
SelectMaxAgeStudentResponse.java
SelectMaxLongNameStudent.java
SelectMaxLongNameStudentResponse.java
其中package-info.java、ObjectFactory.java 是JAXB 需要的文件;HelloServiceImplService.java
继承自javax.xml.ws.Service 类,用于提供WSDL 的客户端视图,里面使用的是大量
javax.xml.ws.*包中的注解;剩下的类是Web 服务的接口、方法参数、响应值的类。
客户端
在 CXF 中使用JaxWsProxyFactoryBean 客户端代理工厂调用Web 服务,代码如下所示:
public class SoapClient {
public static void main(String[] args) throws ParseException {
JaxWsProxyFactoryBean soapFactoryBean = new
JaxWsProxyFactoryBean();
//输出日志的拦截器
soapFactoryBean.getInInterceptors().add(new LoggingInInterceptor());
soapFactoryBean.getOutInterceptors().add(new LoggingOutInterceptor());
soapFactoryBean.setAddress("http://127.0.0.1:8080/helloService");
soapFactoryBean.setServiceClass(IHelloService.class);
Object o = soapFactoryBean.create();
IHelloService helloService = (IHelloService) o;
Customer c1 = new Customer();
c1.setId(1);
c1.setName("A");
GregorianCalendar calendar = (GregorianCalendar)
GregorianCalendar
.getInstance();
calendar
.setTime(new
SimpleDateFormat("yyyy-MM-dd").parse("1989-01-28"));
c1.setBirthday(new XMLGregorianCalendarImpl(calendar));
Customer c2 = new Customer();
c2.setId(2);
c2.setName("B");
calendar
.setTime(new
SimpleDateFormat("yyyy-MM-dd").parse("1990-01-28"));
c2.setBirthday(new XMLGregorianCalendarImpl(calendar));
System.out.println(helloService.selectMaxAgeStudent(c1, c2).getName());
}
}
客户端访问Web 服务时我们使用了CXF 的JaxWsProxyFactoryBean 来进行操作,其实你也可以使用标准的JAX-WS 的API 完成客户端调用。
例如:
QName qName = new QName("http://server.soap.ilkj.net/",
"HelloServiceImplService");
HelloServiceImplService helloServiceImplService =new HelloServiceImplService(
new URL("http://127.0.0.1:8080/ws/services/helloService?wsdl"),
qName);
IHelloService helloService = (IHelloService) helloServiceImplService
.getPort(IHelloService.class);
CXF 与Spring:
CXF 可以很好的与Spring 整合,这样可以为我们省去很多的代码,你需要的是简单的Spring配置即可。
I. CXF发布在Web服务器:
我们前面都是使用CXF 自带的Jetty 发布Web 服务,如果我们的Web 服务在Web 服务器中编写,那么使用现有的Web 服务器自然是更好的选择。
通常CXF 在Web 服务器发布Web 服务都是通过Spring 容器完成,但是CXF 也可以完全脱离Spring 容器独立运行在Web 容器中, 你需要书写一个类覆盖
org.apache.cxf.transport.servlet.CXFNonSpringServlet 的loadBus 方法指定BUS 以及发布你的Web 服务。我们的Web 应用上下文是/ws。
不用Spring时编写自己的CXFNonSpringServlet并配置web.xml
(1.)web.xml:
<servlet>
<servlet-name>CXFServlet</servlet-name>
<servlet-class>
net.ilkj.servlet.MyCXFNonSpringServlet
</servlet-class>
<init-param>
<param-name>/helloService</param-name>
<param-value>
net.ilkj.soap.server.HelloServiceImpl
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
你可以配置多个初始化参数,指定你要发布的Web服务实现类。
(2.)MyCXFNonSpringServlet.java:
public class MyCXFNonSpringServlet extends CXFNonSpringServlet {
/**
*
*/
private static final long serialVersionUID = 1930791254280865620L;
@Override
public void loadBus(ServletConfig servletConfig) throws
ServletException {
super.loadBus(servletConfig);
Bus bus = this.getBus();
BusFactory.setDefaultBus(bus);
// 获取在web.xml中配置的要发布的所有的Web服务实现类并发布Web服务
Enumeration<String> enumeration = getInitParameterNames();
while (enumeration.hasMoreElements()) {
String key = enumeration.nextElement();
String value = getInitParameter(key);
try {
Class clazz = Class.forName(value);
try {
Endpoint.publish(key, clazz.newInstance());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
为了测试结果,你可以暂时先删除lib 目录下Spring 的所有jar 文件,然后我们访问http://127.0.0.1:335/ws/services/helloService?wsdl,我们看到Web 服务发布成功。
2 使用Spring发布SOAP方式的Web服务:
使用Spring 发布Web 服务很简单,首先将web.xml 改为如下的形式:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/beans.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<servlet>
<servlet-name>CXFServlet</servlet-name>
<servlet-class>
org.apache.cxf.transport.servlet.CXFServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
我们看到CXFServlet 负责截获/services/*的请求,Web 服务的SEI 由Spring 的上下文加载监听器来完成查找。那么下面我们来配置Spring,beans.xml 如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xmlns:jaxrs="http://cxf.apache.org/jaxrs"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd
http://cxf.apache.org/jaxrs
http://cxf.apache.org/schemas/jaxrs.xsd">
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml"
/>
<import
resource="classpath:META-INF/cxf/cxf-extension-jaxrs-binding.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
<jaxws:endpoint id="helloServiceWs" address="/helloService"
implementor="#helloService" />
<bean id="helloService"
class="net.ilkj.soap.server.HelloServiceImpl" />
</beans>
我们注意到这里引入了两个新的名称空间jaxws、jaxrs,因为CXF 实现了Spring 的NamespaceHandler 接口,实现这个接口可以在Spring 中增加额外的配置。
那么jaxws 自然是配置SOAP 方式的Web 服务,你可以看到有jaxws:server、jaxws:endpoint、jaxws:client 三个元素,jaxws:server 和jaxws:endpoint 是等效的,都用于发布Web 服务,出现jaxws:endpoint 的原因是JAX-WS 规范中使用EndPoint 发布Web 服务(前面使用过这种方式),CXF 为了和JAX-WS 对应,提供了这个与jaxws:server 功能一样的配置元素;jaxrs是REST 方式的Web 服务,有jaxrs:server、jaxrs:client 两个元素。你也可以使用implementorClass 属性直接指向一个类,而不是像上面那样引用一个Bean 的名字。
我们启动Web 服务器,访问http://127.0.0.1:335/ws/services/helloService?wsdl 地址,如果你
看到WSDL,那么表示我们的Web 服务发布成功。你也可以使用下面的方式发布Web 服务:
<jaxws:server id="helloServiceWs" address="/helloService">
<jaxws:serviceBean>
<ref bean="helloService" />
</jaxws:serviceBean>
</jaxws:server>
同样,你也可以使用serviceClass 属性指向一个类,而不是使用子元素jaxws:serviceBean 引用一个Bean 的名字。无论是哪种方式,指向的目标都是SEI 的实现类。
II. 使用Spring开发SOAP方式的客户端:
<jaxws:client id="helloServiceClient"
address="http://127.0.0.1:335/ws/services/helloService"
serviceClass="net.ilkj.soap.client.IHelloService"/>
这里属性serviceClass 指向客户端生成的接口,这里就不能指向一个Bean 的名字,因为接
口是不能被实例化的。然后你就可以向访问Spring 容器中的Bean 一样去访问这个CXF 的
客户端代理(JaxWsClientProxy),下面是我们在JSP 中书写的调用代码:
IHelloService helloService =
(IHelloService) WebApplicationContextUtils
.getWebApplicationContext(application)
.getBean("helloServiceClient");
out.print(helloService.selectMaxAgeStudent(c1, c2).getName());
更多关于CXF(JAX-WS规范)的令牌,异常处理以及异步调用请参照“JAVA的WebService支持.pdf”文档。双击下面文档打开。
- WebService-CXF
- CXF webservice
- CXF WebService
- WebService CXF
- CXF WebService
- CXF WebService
- CXF webservice
- webservice cxf
- cxf webservice
- cxf webservice
- webservice CXF
- webservice cxf
- CXF WebService
- Webservice-CXF
- Cxf webservice
- WebService cxf
- cxf Webservice
- webservice cxf
- 试题三:古代人的难题( puzzle.pas/c/cpp) 斐波那契数列
- 在没有安装有mvc3的主机上部署asp.net mvc3网站,需要包含的DLL文件
- 【转】Apache CXF入门范例以及对传递List<Map>类型的疑惑
- IBM融入FileNet技术提升ECM能力
- log4cplus代码OO面向对象设计
- WebService-CXF
- 使用eclipse编译android源码
- Android权限之三共享UID和签名
- MATLAB中导入数据:importdata函数
- 中国科大的毕业生去向
- Makefile详解
- 7.9 单词游戏 搜索
- VC中Combo Box控件使用大全
- in和exists的区别与SQL执行效率分析