1.使用CXF创建服务的基本方法(使用CXF自带的jetty容器)
参考:http://cxf.apache.org/docs/a-simple-jax-ws-service.html
分4步:
① 设置build环境
② 写服务
③ 发布服务
④ 访问服务
1)设置build环境
创建一个新项目,将apache-cxf-2.2.4.zip中lib目录中的下列文件添加到Build Path:
commons-logging-1.1.1.jar
geronimo-activation_1.1_spec-1.0.2.jar (or Sun's Activation jar)
geronimo-annotation_1.0_spec-1.1.1.jar (JSR 250)
geronimo-javamail_1.4_spec-1.6.jar (or Sun's JavaMail jar)
geronimo-servlet_2.5_spec-1.2.jar (or Sun's Servlet jar)
geronimo-ws-metadata_2.0_spec-1.1.2.jar (JSR 181)
geronimo-jaxws_2.1_spec-1.0.jar (or Sun's jaxws-api-2.1.jar)
geronimo-stax-api_1.0_spec-1.0.1.jar (or other stax-api jar)
jaxb-api-2.1.jar
jaxb-impl-2.1.12.jar
jetty-6.1.21.jar
jetty-util-6.1.21.jar
neethi-2.0.4.jar
saaj-api-1.3.jar
saaj-impl-1.3.2.jar
wsdl4j-1.6.2.jar
wstx-asl-3.2.8.jar
XmlSchema-1.4.5.jar
xml-resolver-1.2.jar
cxf-2.2.4.jar
可选:添加Spring jars,为XML Configuration添加Spring支持。添加的jars如下:
aopalliance-1.0.jar
spring-core-2.5.5.jar
spring-beans-2.5.5.jar
spring-context-2.5.5.jar
spring-web-2.5.5.jar
2)写服务
A)写接口
@WebService
public interface HelloWorld {
String sayHi(String text);
// JAX-WS/JAXB 不能直接支持高级用例,处理他们需要写特殊的XmlAdapter
String sayHiToUser(User user);
/* Map 传递
* JAXB 不支持 Maps。它能很好的处理Lists,但Maps不能直接支持他们。
* 他们也需要使用一个XmlAdapter来将maps映射进JAXB可以使用的beans
*/
@XmlJavaTypeAdapter(IntegerUserMapAdapter.class)
Map<Integer, User> getUsers();
}
注意:wsdl会重命名参数的名字,如果不希望这样,应该这样写:
@WebService
public interface HelloWorld {
String sayHi(@WebParam(name="text") String text);
}
B)写实现:
package demo.hw.server;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.jws.WebService;
@WebService(endpointInterface = "demo.hw.server.HelloWorld",
serviceName = "HelloWorld") //告诉CXF用哪一个接口创建WSDL
public class HelloWorldImpl implements HelloWorld {
Map<Integer, User> users = new LinkedHashMap<Integer, User>();
public String sayHi(String text) {
System.out.println("sayHi called");
return "Hello " + text;
}
public String sayHiToUser(User user) {
System.out.println("sayHiToUser called");
users.put(users.size() + 1, user);
return "Hello " + user.getName();
}
public Map<Integer, User> getUsers() {
System.out.println("getUsers called");
return users;
}
}
3)发布服务(CXF自带Jetty服务器,所以无需Tomcat就可发布)
A)使用jws的高层封装:
System.out.println("Starting Server");
HelloWorldImpl implementor = new HelloWorldImpl();
String address = "http://localhost:9000/helloWorld";
Endpoint.publish(address, implementor);
B)使用下列代码比较精确地控制服务的行为:
HelloWorldImpl implementor = new HelloWorldImpl();
JaxWsServerFactoryBean svrFactory = new JaxWsServerFactoryBean();
svrFactory.setServiceClass(HelloWorld.class); //可省,但不建议,因为可能会有些小问题
svrFactory.setAddress("http://localhost:9000/helloWorld");
svrFactory.setServiceBean(implementor);
svrFactory.getInInterceptors().add(new LoggingInInterceptor());
svrFactory.getOutInterceptors().add(new LoggingOutInterceptor());
svrFactory.create();
自此,可以通过http://localhost:9000/helloWorld?wsdl来显示该服务的wsdl
LoggingInInterceptor和LoggingOutInterceptor是日志拦截器,用于输入和输出时显示日志,下同。
4)访问服务
A)使用jws的高层封装:
//第一个参数是接口实现类包名的反缀
private static final QName SERVICE_NAME = new QName("http://server.hw.demo/", "HelloWorld");
private static final QName PORT_NAME= new QName("http://server.hw.demo/", "HelloWorldPort");
……
Service service = Service.create(SERVICE_NAME);
// Endpoint Address
String endpointAddress = "http://localhost:9000/helloWorld";
// Add a port to the Service
service.addPort(PORT_NAME, SOAPBinding.SOAP11HTTP_BINDING, endpointAddress);
HelloWorld hw = service.getPort(HelloWorld.class);
System.out.println(hw.sayHi("World"));
B)或者使用下面代码更精确的控制服务:
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.getInInterceptors().add(new LoggingInInterceptor());
factory.getOutInterceptors().add(new LoggingOutInterceptor());
factory.setServiceClass(HelloWorld.class);
factory.setAddress("http://localhost:9000/helloWorld");
HelloWorld client = (HelloWorld) factory.create();
String reply = client.sayHi("HI");
System.out.println("Server said: " + reply);
System.exit(0);
2.wsdl2java:从wsdl文档中生成java类,供client使用
设置环境变量CXF_HOME=D:\Program Files\apache-cxf-2.2.4,PATH后加上“;%CXF_HOME%\bin”(可选),然后执行wsdl2java批处理程序,用法如下:
wsdl2java –p 包名 –d 目录名 wsdl路径
如:wsdl2java –p demo.service.client –d e:\src htt://localhost:8080/helloWorld?wsdl
-p 指定其wsdl的命名空间,也就是要生成代码的包名
-d 指定要产生代码所在目录
-client 生成客户端测试web service的代码
-server 生成服务器启动web service的代码
-impl 生成web service的实现代码
-ant 生成build.xml文件
-all 生成所有开始端点代码:types,service proxy,,service interface, server mainline, client mainline, implementation object, and an Ant build.xml file.
详细用法见http://cwiki.apache.org/CXF20DOC/wsdl-to-java.html
3.定义复杂类型(基本类型如int,String,无须额外定义),参考资料:http://cxf.apache.org/docs/defining-contract-first-webservices-with-wsdl-generation-from-java.html
例如:
package com.example.customerservice;
@XmlAccessorType( XmlAccessType.FIELD )
public class Customer { //自定义类
String name;
String\[\] address;
int numOrders;
double revenue;
BigDecimal test;
Date birthDate;
CustomerType type; //自定义枚举类型
}
public enum CustomerType {
PRIVATE, BUSINESS
}
//定义Exception
@WebFault(name="NoSuchCustomer")
@XmlAccessorType( XmlAccessType.FIELD )
public class NoSuchCustomerException extends RuntimeException {
/**
* We only define the fault details here. Additionally each fault has a message
* that should not be defined separately
*/
String customerName;
} //定义Exceptions的默认行为是在后面生成Java code时创建Exception_Exception,所以必须用@WebFault标记来为Bean取一个名字,以与Exception名字相区别
@WebService //标记本接口为一个服务
public interface CustomerService {
public Customer[] getCustomersByName(@WebParam(name="name") String name) throws NoSuchCustomerException; //@WebParam标记wsdl中的参数名。如果省略,wsdl将使用arg0代替
}
// @WebService还可用来自定义接口名和服务名,分别对应:endpointInterface和serviceName,如:
@WebService(endpointInterface = "com.example.customerservice", serviceName = "HelloWorld")
生成的WSDL:
<xs:complexType name="customer"> //复杂类型
<xs:sequence>
<xs:element minOccurs="0" name="name" type="xs:string"/>
<xs:element maxOccurs="unbounded" minOccurs="0" name="address" nillable="true" type="xs:string"/>
<xs:element name="numOrders" type="xs:int"/>
<xs:element name="revenue" type="xs:double"/>
<xs:element minOccurs="0" name="test" type="xs:decimal"/>
<xs:element minOccurs="0" name="birthDate" type="xs:dateTime"/>
<xs:element minOccurs="0" name="type" type="tns:customerType"/>
</xs:sequence>
</xs:complexType>
minOccurs="0"是可选项,这样可以随时加入新元素,保持兼容性。如果不想要这个选项,可以使用标记@XmlElement(required=true)
maxOccurs="unbounded"是为了便于后面的xml重复该元素以形成数组。
<xs:simpleType name="customerType"> //枚举类型
<xs:restriction base="xs:string">
<xs:enumeration value="PRIVATE"/>
<xs:enumeration value="BUSINESS"/>
</xs:restriction>
</xs:simpleType>
<xs:element name="NoSuchCustomer" type="tns:NoSuchCustomer"/> //异常类
<xs:complexType name="NoSuchCustomer">
<xs:sequence>
<xs:element name="customerName" nillable="true" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<wsdl:message name="NoSuchCustomerException">
<wsdl:part name="NoSuchCustomerException" element="tns:NoSuchCustomer">
</wsdl:part>
</wsdl:message>
// 注意:Element和Message的名字是不同的,这是通过标记@Webfault标记来实现的。也可以让他们同名,但那样会话,生成的Exception的名字会比较丑:NoSuchCustomerException_Exception