规范
规范只是接口,如JAX-WS和JAX-RS有CXF具体实现,有三种规范:JAXM&SAAJ(暴露了更多细节),JAX-WS和JAX-RS(rest风格的服务规范)。
JAX-WS(JAX-RPC):
底层使用JAXB,JAX-RPC已被JAX-WS替换,位于javax.xml.ws.* 提供api操作web服务。 WS-MetaData:是JAX-WS的依赖规范,位于javax.jws.*
实例
1. Service EndPoint Interface
(使用@Method和@WebService 标注接口为web服务)
@WebService
public interface IHelloService {
Customer selectMaxAgeStudent(Customer c1, Customer c2);
Customer selectMaxLongNameStudent(Customer c1, Customer c2);
}
2. 实现类:
类如果实现有多个接口,要使用 @WebService endpointInterface 指定哪个接口是SEI
3. 参数:
SEI中参数的类,使用JAXB注解告诉CXF 完成XML和Java Object间处理,如@XmlRootElement。
@XmlRootElement(name = "Customer")
public class Customer {
private long id;
private String name;
private Date birthday;
}
4. 发布服务:
使用 javax.xml.ws.Endpint.publish 发布服务,通过 url?wsdl可查看生成的wsdl文件
public static void main(String[] args) {
Endpoint.publish("http://127.0.0.1:8080/helloService",
new HelloServiceImpl());
}
5. 查看wsdl
访问 http://127.0.0.1:8080/helloService?wsdl地址
分析WSDL的构成
<wsdl:definitions :
name(实现类+Service),targetNamespace
<wsdl:type:
Xs:element:
web service中complex数据类型的元素定义
Xxx(方法名称)和XxxResponse:对参数和返回值的元素定义
<xs:complexType:
通过name关联到 xs:element,为前面定义的元素指定具体的封装内容
<wsdl:message
将参数、返回值和异常封装为消息
<wsdl:portType
Name为接口名称
<wsdl:operation :哪些方法,<wsdl:input <wsdl:output 指定操作的输入输出,使用message绑定前面声明过的消息。
<wsdl:binding
绑定webservice到SOAP协议,指定消息封装格式、发布地址
<wsdl:service
Name: 服务名称,
<wsdl:port name指定port名称,
<soap:address location指定web服务的地址
实例如下:
<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>
可以在SEI中指定参数和返回值的名称,如:
@WebResult(name = "method")
Customer selectMaxAgeStudent(@WebParam(name = "c1") Customer c1,
@WebParam(name = "c2") Customer c2);
客户端
1. 根据wsdl生成java文件
常用的方式就是 wsdljava –p 包路径–d 目标文件夹 wsdl 的url,如
wsdl2java -p net.ilkj.soap.client –d E:\ http://127.0.0.1:8080/helloService?wsdl
2. 使用JaxWsProxyFactoryBean创建webservice的代理进行调用。
public static void main(String[] args) throws ParseException {
JaxWsProxyFactoryBean soapFactoryBean = new JaxWsProxyFactoryBean();
soapFactoryBean.setAddress("http://127.0.0.1:8080/helloService");
soapFactoryBean.setServiceClass(IHelloService.class);
IHelloService helloService = (IHelloService) soapFactoryBean.create();
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());
}
当wsdl可以访问,webservice 不一定能访问,还和web service的实现有关,wsdl只是接口的一种xml表示
SOAP消息格式
将【实例】中的发布改为使用 JaxWsServerFactoryBean,加入日志拦截器到输入和输出拦截器中。
1: public static void main(String[] args) {
2: JaxWsServerFactoryBean soapFactoryBean = new JaxWsServerFactoryBean();
3: soapFactoryBean.getInInterceptors().add(new LoggingInInterceptor());
4: soapFactoryBean.getOutInterceptors().add(new LoggingOutInterceptor());
5: // 注意这里是实现类不是接口
6: soapFactoryBean.setServiceClass(HelloServiceImpl.class);
7: soapFactoryBean.setAddress("http://127.0.0.1:8080/helloService");
8: soapFactoryBean.create();
9:
10: }
11:
效果如下:
<soap:Envelope
<soap:header
<soap:Body
Inbound Message输出的是服务器端接收到的 SOAP 信息,Outbound Message输出的服务器端响应的 SOAP 信息,SOAP 的 Headers:{}的前面是 SOAP 消息的标识、编码方式、MIME类型,Headers:{}熟悉 HTTP 应该很容易看懂这里面的消息报头的作用,Headers:{}后面的Payload(有效负载,也叫净荷)的 XML 就是 SOAP 消息的真正内容,我们看到 SOAP 消息内容被封装为<soap:Envelope …SOAP 信封,在信封之间的内容就是 SOAP 消息正文。
输入和输出参数
对参数使用 @WebParam(name=’c2’ mode=Mode.OUT) 可定义参数显示的名称为c2,且为out类型的. Mode可以有IN,OUT,INOUT类型,对于后两种会作为返回值,客户端生成代码是Holder<Customer> c3
服务端 SEI:
@WebService
public interface IHelloService {
boolean selectMaxAgeStudent(@WebParam(name = "c1") Customer c1, @WebParam(name = "c2") Customer c2,
@WebParam(name = "c3", mode = Mode.OUT) Holder<Customer> c3);
Customer selectMaxLongNameStudent(Customer c1, Customer c2);
}
客户端调用代码:
public static void main(String[] args) throws ParseException {
JaxWsProxyFactoryBean client = new JaxWsProxyFactoryBean();
client.setAddress("http://127.0.0.1:335/ws/services/helloService");
client.setServiceClass(IHelloService.class);
IHelloService helloService = (IHelloService) client.create();
….
Holder<Customer> ch = new Holder<Customer>();
helloService.selectMaxAgeStudent(c1, c2, ch);
@javax.jws.Oneway 表示公开的web service的方法没有任何返回值
Web service context
在实现类中访问请求相关的Message Context,只要使用 javax.annotation.Resouce标注就可使用该接口,如下:
@Resource
Private WebServiceContext context;
//调用 context.getMessageContext() 后以Map形式进行遍历,得到属性值,使用mCOntext. getScope得到属性范围
客户端视图
使用标准的JAX-WS来完成客户端调用,例:
// 使用wsdl中的targetNamespace和<wsdl:service name构建QName接口
QName qName=new QName("http://server.soap.ilkj.net/","HelloServiceImplService");
//实现javax.xml.ws.Service的客户端视图类
HelloServiceImplService helloServiceImplService=new HelloServiceImplService(
new URL("http://127.0.0.1:8080/ws/services/helloService?wsdl"),qName);
//找到端口服务接口
IHelloService helloService=(IHelloService)helloServiceImplService.getPort(IHelloService.class);
异常处理
使用javax.xml.ws.WebFault注解,这样异常就会在 <wsdl:operation的<wsdl:fault 中,如下:
@WebFault(name="HelloServiceException")
public class HelloServiceException extends Exception{
private static final long serialVersionUID=1562884941631450124L;
private HelloServiceFault details;
public HelloServiceException(String msg){
super(msg);
}
public HelloServiceException(String msg,HelloServiceFault details)
{
super(msg);
this.details=details;
}
public HelloServiceException(HelloServiceFault details){
super();
this.details=details;
}
public HelloServiceFault getFaultInfo(){return details;
}
@XmlRootElement(name="HelloServiceFault")
public static class HelloServiceFault{
private String t;
public HelloServiceFault(){
}
public HelloServiceFault(String t){
this.t=t;
}
public String getT(){
return t;
}
public void setT(String t){
this.t=t;
}
}
注意:自定义异常包含异常消息msg和封装错误信息的bean,这个bean必须使用JAXB注释。 自定义异常必须有getFaultInfo()返回封装具体错误信息的bean
MTOM
在消息中传送二进制信息,需要XOP传输二进制数据。否则附件会被base64编码传递,会答很多。
1. 创建二进制属性
@XmlRootElement(name = "Customer")
@XmlAccessorType(XmlAccessType.FIELD) //标注xml和java转换时只关注字段
public class Customer {
private long id;
private String name;
private Date birthday;
@XmlMimeType("application/octet-stream")
private DataHandler imageData;
生成的wsdl如下:
<xs:element minOccurs="0" name="imageData" ns1:expectedContentTypes="application/octet-stream" type="xs:base64Binary" xmlns:ns1="http://www.w3.org/2005/05/xmlmime" />
2. 服务器和客户端启用XTOM:
Spring如下:
<jaxws:properties>
<entry key="mtom-enabled" value="true" />
</jaxws:properties>
这段内容加到<jaxws:server … 、<jaxws:endpoint … 、<jaxws:client … 之间即可。
Java 代码:
在服务端、客户端获取javax.xml.ws.soap.SoapBinding 实例,然后调用它的 setMTOMEnabled(true)方法。
3.传输数据
服务端:
rs.setImageData(new DataHandler(new FileDataSource( new File("c:"+ File.separator + "18.jpg"))));
客户端:
String attachmentMimeType = helloService.selectMaxLongNameStudent(c1, c2).getImageData().getDataSource().getContentType();
JAXRS
REST中重要的两个概念就是资源定位和资源操作,而HTTP 协议恰好完整的提供了这两个要点,HTTP 协议中的 URI 可以完成资源定位,GET、POST、PUT DELETE等方法可以完成资源操作,因此 REST 完全依赖 HTTP 协议就可以完成 Web 服务,而不像SOAP 协议那样只利用HTTP 的传输特性,定位与操作由 SOAP 协议自身完成。
实例
1. 定义SEI
@Path(value = "/student/{id}")
@Produces("application/xml")
public interface IStudentService {
@GET
@Path(value = "/info")
Student getStudent(@PathParam("id") long id, @QueryParam("name") String name);
@GET
@Path(value = "/info2")
Student getStudent(@QueryParam("name") String name);
}
说明:
1.这个REST 的服务接口的最终响应结果是 XML(@Produces注解标注,这个注解可以包含一组字符串,默认值是*/*,它指定 REST 服务的响应结果的 MIME 类型,例如:application/xml、application/json、image/jpeg 等),你也可以同时返回多种类型,但具体生成结果时使用哪种格式取决于ContentType。CXF默认返回的是JSON 字符串。
2.访问方法URI是/student/1/info?name=Andrew-Lee、/student/1/info2?name=Fetion,由@Path注解组合而来;
3.@QueryParam注解用于指定将 URL上的查询参数传递给使用这个注解的属性值;
4.@PathParam注解用于指定将URL上的路径参数作为使用这个注解的属性值。
5.@GET 注解指定方法对应于 Http 的 GET 请求。
2. 实现和参数
实现和参数同JAXWS
3. 发布服务
public static void main(String[] args) {
JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
sf.setResourceClasses(StudentServiceImpl.class);
sf.setAddress("http://localhost:335/");
sf.create();
}
4. 客户端访问
((HttpURLConnection)new URL(“***”).openConnection()).getInputStream()获取
使用httpclient访问
和JAXWS区别
1. REST面向资源的服务,SOAP面向活动的服务。
2. REST 简单易用,效率高,SOAP 成熟度较高,安全性较好。
方法返回值
通过Response返回http响应代码、响应头或者是一种实体
1. 服务接口
@Path(value = "/student/{id}")
@Produces("application/xml")
public interface IStudentService {
@GET
@Path(value = "/info")
Response getStudent(@PathParam("id") long id,
@QueryParam("name") String name);
@GET
@Path(value = "/info2")
Response getStudent(@QueryParam("name") String name);
}
2. 服务实现
public class StudentServiceImpl implements IStudentService {
public Response getStudent(long id, String name) {
Student s = new Student();
s.setId(id);
s.setName(name);
try {
s.setBirthday(new SimpleDateFormat("yyyy-MM-dd")
.parse("1983-04-26"));
} catch (ParseException e) {
e.printStackTrace();
}
return Response.ok(s).build(); //响应实体
}
public Response getStudent(String name) {
return Response.status(Response.Status.BAD_REQUEST).build(); //响应代码
}
}
3. 客户端访问
HttpResponse response = httpclient.execute(get);
StatusLine st = response.getStatusLine();
if (st.getStatusCode() == HttpServletResponse.SC_OK) {
InputStream ins = response.getEntity().getContent();
异常处理
参数处理
生命周期
Context注释
使用@javax.ws.rs.core.Context 注解将 UriInfo, SecurityContext, HttpHeaders, Providers, Request, ContextResolver, HttpServletRequest, HttpServletResponse, ServletContext, ServletConfig 实例注入到服务实现类
Webclient
Apache-Components-Client 要比 java.net.*下面的 API 要来得简单),其实pache.cxf.jaxrs.client.WebClient用起来更加简单。例:
WebClient client = WebClient.create("http://127.0.0.1:8080/ws/services/student/1/");
Student student = client.path("info/matrix;id=2;name=m.j").accept( "application/xml").get(Student.class);
System.out.println(student.getName());
CXF和spring
服务端
1. 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>
2. 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:server id="helloServiceWs" address="/helloService">
<jaxws:serviceBean>
<ref bean="helloService" />
</jaxws:serviceBean>
</jaxws:server>
<bean id="helloService" class="net.ilkj.soap.server.HelloServiceImpl" />
</beans>
客户端
<jaxws:client id="helloServiceClient" address="http://127.0.0.1:335/ws/services/helloService"
serviceClass="net.ilkj.soap.client.IHelloService"/>
IHelloService helloService =BeanUtils.getBean(“helloServiceClient”);
Spring发布rest风格
SOAP的WS-*规范
WS-Addressing:
与传输协议的隔离,寻址方式采用基于消息的路由,实现会话状态的保存
WS-Reliable Messaging:
可靠消息传递
WS-Security和WS-Policy、WS-Trust :
安全策略和信任机制
用户名令牌机制
Apache的WSS4J实现了Ws-Security,WSS4J依赖于SAAJ。
CXF 中使用拦截器机制完成 WSS4J 功能的支持,你只需要初始化 WSS4JInInterceptor(对 应的还有一个 WSS4JOutInterceptor)实例并添加相关信息即可
1. 服务器端配置:
<bean id="wss4jInInterceptor" class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
<constructor-arg>
<map>
<entry key="action" value="UsernameToken" />
<entry key="passwordType" value="PasswordText" />
<entry key="passwordCallbackClass"
value="net.ilkj.soap.server.security.ServerPasswordCallbackHandler" />
</map>
</constructor-arg>
</bean>
<jaxws:server id="helloServiceWs" address="/helloService">
<jaxws:serviceBean>
<ref bean="helloService" />
</jaxws:serviceBean>
<jaxws:inInterceptors>
<ref bean="wss4jInInterceptor" />
</jaxws:inInterceptors>
</jaxws:server>
Constructor-arg中传入的参数在org.apache.ws.security.handler.WSHandlerConstants和
org.apache.ws.security.WSConstants中的常量列表中查找。例如:上面的第一组键值对 action
和 UsernameToken 都是 WSHandlerConstants 中的常量,表示验证机制是用户姓名令牌,也就是使用传统的用户名和密码机制。第二组的键值对分别是 WSHandlerConstants 和WSConstants中的常量,表示密码类型是文本,还可以是WSConstants.PASSWORD_DIGEST
(密码会被加密为 MD5)。第三组键值对的键表示服务器端验证密码的回调处理类,这个
类必须JAVA安全认证框架中的 javax.security.auth.callback.CallbackHandler类
2. 服务器端java
public class ServerPasswordCallbackHandler implements CallbackHandler {
public final static String USER = "Fetion2";
public final static String PASSWORD = "Fetion";
@Override
public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
WSPasswordCallback wspassCallback = (WSPasswordCallback)
callbacks[0];
System.out.println(wspassCallback.getIdentifier() + "\t"
+ wspassCallback.getPassword());
if (wspassCallback.getIdentifier().equals(USER)
&& wspassCallback.getPassword().equals(PASSWORD)) {
// undo
} else {
throw new WSSecurityException("No Permission!");
}
}
}
3. 客户端配置
<bean id="wss4jOutInterceptor"
class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor">
<constructor-arg>
<map>
<entry key="action" value="UsernameToken" />
<entry key="user" value="Fetion" />
<entry key="passwordType" value="PasswordText" />
<entry key="passwordCallbackClass"
value="net.ilkj.soap.client.security.ClientPasswordCallbackHandle
r" />
</map>
</constructor-arg>
</bean>
<jaxws:client id="helloServiceClient"
address="http://127.0.0.1:335/ws/services/helloService"
serviceClass="net.ilkj.soap.client.IHelloService">
<jaxws:outInterceptors>
<ref bean="wss4jOutInterceptor" />
</jaxws:outInterceptors>
</jaxws:client>
4. 客户端代码
public class ClientPasswordCallbackHandler implements CallbackHandler {
public final static String USER = "Fetion2";
public final static String PASSWORD = "Fetion";
@Override
public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
WSPasswordCallback wspassCallback = (WSPasswordCallback)
callbacks[0];
wspassCallback.setIdentifier(USER);
wspassCallback.setPassword(PASSWORD);
}
}
MD5传递
1. 客户端和服务端配置文件
beans.xml中只需要将passwordType的值变为PasswordDigest即可
2. 客户端代码不变
3. 服务器端代码
@Override
public void handle(Callback[]callbacks)throws IOException,
UnsupportedCallbackException{
WSPasswordCallback wspassCallback=(WSPasswordCallback)callbacks[0];
System.out.println(wspassCallback.getIdentifier()+"\t"
+wspassCallback.getPassword());
if(WSConstants.PASSWORD_TEXT.
equals(wspassCallback.getPasswordType())){
if(wspassCallback.getIdentifier().equals(USER)
&&wspassCallback.getPassword().equals(PASSWORD)){
//undo
}else{
throw new WSSecurityException("No Permission!");
}
}else{
System.out.println(wspassCallback.getIdentifier());
//一般使用这个用户名到数据库中查询其密码,然后再设置到password属性,WSS4J会自动比较客户端传来的值和你设置的这个值。
wspassCallback.setPassword(PASSWORD);
}
}
证书
1. 生成客户端和服务端的证书文件
分别在两端生成相应的公钥和私钥, 批储量文件如下:
(1.)generateKeyPair.bat:
rem@echo off
echo alias%1
echo keypass%2
echo keystoreName%3
echo KeyStorePass%4
echo keyName%5
keytool-genkey-alias%1-keypass%2-keystore%3-storepass%4-dname"cn=%1"-keyalg RSA
keytool-selfcert-alias%1-keystore%3-storepass%4-keypass%2
keytool-export-alias%1-file%5-keystore%3-storepass%4
(2.) generateServerKey:
call generateKeyPair.bat apmserver apmserverpass serverStore.jks keystorePass serverKey.rsa
pause
call generateKeyPair.bat apmclient apmclientpass clientStore.jks keystorePass clientKey.rsa
pause
keytool-import-alias apmserver-file serverKey.rsa-keystore clientStore.jks-storepass keystorePass
pausekeytool-import-alias apmclient-file clientKey.rsa-keystore serverStore.jks-storepass keystorePass
2. 得到clientStore.jks和serverStore.jks文件
为两个jks分别建立相应的properties文件,如下:
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.
pto.Merlin
org.apache.ws.security.crypto.merlin.keystore.type=jks
org.apache.ws.security.crypto.merlin.keystore.password=keystorePass
#org.apache.ws.security.crypto.merlin.alias.password=apmserverpass
org.apache.ws.security.crypto.merlin.keystore.alias=apmserver
org.apache.ws.security.crypto.merlin.file=serverStore.jks
client_sign.properties:
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.
pto.Merlin
org.apache.ws.security.crypto.merlin.keystore.type=jks
org.apache.ws.security.crypto.merlin.keystore.password=keystorePass
#org.apache.ws.security.crypto.merlin.alias.password=apmclientpass
org.apache.ws.security.crypto.merlin.keystore.alias=apmclient
org.apache.ws.security.crypto.merlin.file=clientStore.jks
3. 修改beans.xml 文件
<bean id="wss4jInInterceptor"
class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
<constructor-arg>
<map>
<entry key="action"value="Signature"/>
<entry key="user"value="apmclient"/>
<entry key="passwordCallbackClass"
value="net.ilkj.soap.server.security.ServerPasswordCallbackHandle
r"/>
<entry key="signaturePropFile"
value="server_sign.properties"></entry></map>
</constructor-arg>
</bean>
<bean id="wss4jOutInterceptor"
class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor">
<constructor-arg>
<map>
<entry key="action"value="Signature"/>
<entry key="user"value="apmclient"/>
<entry key="passwordCallbackClass"
value="net.ilkj.soap.client.security.ClientPasswordCallbackHandle
r"/>
<entry key="signaturePropFile"
value="client_sign.properties"></entry>
</map>
</constructor-arg>
</bean>
4. 密码回调处理类
public class ClientPasswordCallbackHandler implements CallbackHandler{
@Override
public void handle(Callback[]callbacks)throws IOException,
UnsupportedCallbackException{
WSPasswordCallback wspassCallback=(WSPasswordCallback)
callbacks[0];
wspassCallback.setPassword("apmclientpass");}
}
5. 调用webservice
helloService.selectMaxAgeStudent(c1,c2).getName()
如果日志中有很多的 <ds: 元素,里面封装的就是数字签证的信息
Transport
前面的CXFServlet和CXFNonSpringServlet就是一种ServletTransport,CXF使用这两个传输端口发布Web服务。
可以配置http的相关设置,如超时时间、SSL相关设置、是否启用缓存等
客户端设置:
<http-conf:conduit name="*.http-conduit">
<http-conf:client ConnectionTimeout="5000" ReceiveTimeout="10000"/>
</http-conf:conduit>
服务端设置:
<http-conf:destination name="*.http-destination">
<http-conf:server ReceiveTimeout="10000"/>
</http-conf:destination>
拦截器特征
CXF通过拦截器(Interceptor)和特征(Feature)扩展自己的功能,例如:WS-Addressing功能实用Feature实现,日志、WS-Security使用Interceptor实现。
我们也可以编写自己的拦截器注册到CXF中完成特定的功能。CXF中的所有拦截器都要事先org.apache.cxf.inrerceptor.Interceptor<T extends org.apache.cxf.message.Message>接口,Message接口可以获得SOAP消息的相关信息。
JAX-WS的异步调用
不需要等待服务端的返回
1. 生成客户端接口
创建 async_binding.xml文件
<bindings
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
wsdlLocation="http://127.0.0.1:8080/ws/services/helloService?wsdl"
xmlns="http://java.sun.com/xml/ns/jaxws">
<bindings node="wsdl:definitions">
<enableAsyncMapping>true</enableAsyncMapping>
</bindings>
</bindings>
给wsdl2java传入-b async_binding.xml 生成客户端的异步stub接口,其中关于异步调用的方法如下:
public Response<SelectMaxLongNameStudentResponse>
selectMaxLongNameStudentAsync(Customer arg0,Customer arg1);
public Future<?>selectMaxLongNameStudentAsync(Customer arg0,
Customer arg1,
AsyncHandler<SelectMaxLongNameStudentResponse>asyncHandler);
2. 两种异步调用方式:
Polling: (返回结果为Response<T> ,称为轮询方式);
Callback:(返回结果是Future,称为回调方法,需额外编写AsyncHandler的回调方法)
代码实例:
HelloAsyncHandler类:
public class HelloAsynchHandler implements
AsyncHandler<SelectMaxAgeStudentResponse> {
private SelectMaxAgeStudentResponse reply;
@Override
public void handleResponse(Response<SelectMaxAgeStudentResponse> res) {
try {
System.out.println("handleResponse called");
reply = res.get();
} catch (Exception ex) {
ex.printStackTrace();
}
}
public Customer getResponse() {
return reply.getReturn();
}
}
客户端调用代码:
//Callback
HelloAsynchHandler helloAsyncHandler=new HelloAsynchHandler();
Future<?>response=helloService.selectMaxAgeStudentAsync(c1,c2,helloAsyncHandler);
System.out.println("Other Things...");
while(!response.isDone()){
Thread.sleep(100);
}
resp=helloAsyncHandler.getResponse();
System.out.println("Server responded through callback with:"+resp.getName());
System.out.println("-----------------------------");
//polling method
Response<SelectMaxAgeStudentResponse>selectMaxAgeStudentResponse=helloService.selectMaxAgeStudentAsync(c1,c2);
System.out.println("Other Things...");
while(!selectMaxAgeStudentResponse.isDone()){
Thread.sleep(100);
}
SelectMaxAgeStudentResponse reply=selectMaxAgeStudentResponse.get();
System.out.println("Server responded through polling with:"+reply.getReturn().getName());
System.exit(0);
SAAJ
应用场景
你访问的Web服务传回来的SOAP消息中的XML可能无法正确解析成你的客户端对象,或者你要对SOAP消息中的XML做一些处理,在javax.xm.soap.*包中
使用方法
客户端:创建SOAP链接——》创建SOAP消息——》增加数据——》发送消息——》对SOAP消息应答
JAXM
定义了发送和接收消息的API,相当于web服务的服务器端,位于javax.messaging.*包中。
发布
将一个servlet发布为一个web service的地址,要求servlet如下:
例:public class MyJAXMServlet extends JAXMServlet implements ReqRespListener{
在onMessage中实现业务方法
JAXM发布的Web服务比较简单,完全省略了WSDL,这也就是说,你用这种方式发布Web服务,必须把要接收的Soap消息的内容说明发布出来(有点儿类似于REST风格的OpenAPI),这样客户端才知道如何组装你想要的SOAP消息。从这里你也可以看出来,HTTP协议与SOAP消息是基于SOAP的基本组成,WSDL是完全可以没有的,WSDL的作用是异构平台为了方便使用自己的语言特性的中间桥梁。
调用
只能使用SoapConnection的call()方法调用。
XSLT
文件实例
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" omit-xml-declaration="yes"/>
<xsl:template match="/">
<html>
<head><title>Hello!</title></head>
<body>
<h1>My First Words</h1>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="word">
<xsl:value-of select="."/><br/>
</xsl:template>
</xsl:stylesheet>