CXF的web service 学习笔记

规范

     规范只是接口,如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>

你可能感兴趣的:(CXF的web service 学习笔记)