笔者前言:笔者是想着用CXF来开发点东西的,没想到比AXIS2的会话管理曲折那么多。可能是笔者对基本的规范和协议不甚了解吧。以下部分转贴,代码基本是笔者改写的。即使如此,笔者认为还是标题为转贴内容吧。
一。前言
本文不是JAX-WS的入门文档,如需了解JAX-WS,请参阅其它文章。
WebService为客户端与服务器沟通提供了非常良好的开发体验,由其在Java中体现尤为明显。JAX-WS是Java世界里一个非常优秀的WebService开发工具,通过采用JAX-WS,开发人员可以用非常简单的方式从客户端调用服务器开放的服务。
在采用C/S模式开发的时候,一个客户与服务器经常经过好几次的交互过程才能完成一笔交易或者是一个请求的完成。由于这几次交互过程是密切相关的,服务器在进行这些交互过程的某一个交互步骤时,往往需要了解上一次交互过程的处理结果,或者上几步的交互过程结果,这就需要在这几次交互过程中保持会话状态。
基于HTTP的应用开发中,要在多个调用之间保持会话状态,通常可以采用以下几种方式:
l URL重写,把要传递的参数重写在URL中;
l 使用Cookie,把要传递的参数写入到客户端cookie中;
l 使用隐含表单,把要传递的参数写入到隐含的表单中;
l 使用Session,把要传递的参数保存在session对象中(其实Session机制基于cookie或者URL重写)。
上面几个方式有一个共同点:把要传递的参数保存在两个页面都能共享的对象中,前一个页面在这个对象中写入状态、后一个页面从这个对象中读取状态。特别是对于使用session方式,每个客户端在服务端都对应了一个sessionid,服务端维持了由sessionid标识的一系列session对象,而session对象用于保持共享的信息
上述保持会话状态的方式,较常用的是服务器Session技术。服务端程序通过调用session.setAttribute(key,value) 和 session.getAttribute(key) ,在多次交互过程中保持会话状态。另外,为了保持会话,客户端也要做一些相应的工作。下面将从两个方面讨论在使用JAX-WS的项目中,如何才能保持会话状态。
二。服务器端代码
2.1 WEB SERVICE(HelloWorld)
package com.zx.cxf.service; import java.util.Date; import java.util.List; import javax.jws.WebParam; import javax.jws.WebService; //注解生命此为WEBSERVICE @WebService public interface HelloWorld { //接受HTTP传过来的参数TEXT为这个方法的参数值 String sayHi(@WebParam(name = "text") String text); Date curDate(); List<Long> addNumber(@WebParam(name = "addToNum") Long num); }
2.2接口的实现(HelloWorldImpl)
package com.zx.cxf.service; import java.util.ArrayList; import java.util.Date; import java.util.List; import javax.annotation.Resource; import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebService; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import javax.xml.ws.WebServiceContext; import javax.xml.ws.handler.MessageContext; import org.apache.cxf.transport.http.AbstractHTTPDestination; import org.springframework.beans.factory.annotation.Autowired; @WebService(endpointInterface = "com.zx.cxf.service.HelloWorld", serviceName = "HelloWorld") public class HelloWorldImpl implements HelloWorld { private List<Long> localNumbers; //通过注解,自动注入如下需要用到的变量(没用到setter方法哦.不过没错误) @Resource private WebServiceContext context; private HttpServletRequest request; //通过注解,声明定义一些列WEBSERVICE的东西.笔者忘记怎么说了,详细调查后补上 @WebMethod(operationName="sayHello",action="hello") public String sayHi(@WebParam(name = "text") String text) { //获得MessageContext上下文,在WEBSERVICE中,是用这个上下文的. MessageContext ctx = context.getMessageContext(); /*HttpServletRequest request = (HttpServletRequest) ctx.get(AbstractHTTPDestination.HTTP_REQUEST);*/ //获得HTTP的SESSION HttpSession session =((javax.servlet.http.HttpServletRequest)ctx.get(MessageContext.SERVLET_REQUEST)).getSession(); //这只是一个统计变量 Integer count = (Integer)session.getAttribute("count"); String change = null; /** * 判断客户端是第几次访问,用以测试SESSIONID究竟有没改变. * 这是配合客户端中,对多个SERVICE中保持会话状态的测试. * 这2种测试,在客户端代码中会注解说明. */ if(count == null){ session.setAttribute("count", 1); }else{ count++; if(count >= 2) change = "你是第二次访问了"; } //HttpSession session = request.getSession(); return "Hello " + text + "sessionID: "+session.getId()+" "+change; } @WebMethod(exclude=true) public Date curDate() { return new Date(); } public List<Long> addNumber(@WebParam(name = "addToNum") Long num) { if (localNumbers == null) { localNumbers = new ArrayList<Long>(); } localNumbers.add(num); return localNumbers; } public void setRequest(HttpServletRequest request){ this.request = request; } }
2.3补发SPRING的配置代码
<?xml version="1.0" encoding="UTF-8"?> <!-- START SNIPPET: beans --> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws" 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"> <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-servlet.xml" /> <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/> <bean id="serverPasswordCallback" class="cxf.server.ServerPasswordCallback" /> <!--<jaxws:endpoint id="helloWorld" implementor="com.zx.cxf.service.HelloWorldImpl" address="/HelloWorld" > --> <jaxws:endpoint id="helloWorld" address="/HelloWorld" > <jaxws:implementor> <bean class="com.zx.cxf.service.HelloWorldImpl"></bean> </jaxws:implementor> <!--<jaxws:inInterceptors> <bean class="org.apache.cxf.interceptor.LoggingInInterceptor" /> SAAJInInterceptor只在CXF是2.0.X版本时或之前版本时才是必须的 <bean class="org.apache.cxf.binding.soap.saaj.SAAJInInterceptor"/> <bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor"> <constructor-arg> <map> <entry key="action" value="UsernameToken" /> <entry key="passwordType" value="PasswordText" /> <entry key="user" value="FHDServer" /> <entry key="passwordCallbackRef"> <ref bean="serverPasswordCallback" /> </entry> </map> </constructor-arg> </bean> </jaxws:inInterceptors> --></jaxws:endpoint> </beans> <!-- END SNIPPET: beans -->
三。客户端代码
3.1 在单个SERVICE中保持会话状态的客户端代码
package clients; import java.net.URL; import java.util.List; import javax.xml.namespace.QName; import javax.xml.ws.Binding; import javax.xml.ws.BindingProvider; import javax.xml.ws.handler.Handler; import com.zx.cxf.handle.Myhandle; import com.zx.cxf.service.HelloWorld; import com.zx.cxf.service.HelloWorld_Service; public class TestHello { private static final QName SERVICE_NAME = new QName("http://service.cxf.zx.com/", "HelloWorld"); public static final Myhandle handler = new Myhandle(); /** * @param args * @throws InterruptedException */ public static void main(String[] args) throws InterruptedException { URL wsdlURL = HelloWorld_Service.WSDL_LOCATION; HelloWorld_Service ss = new HelloWorld_Service(wsdlURL, SERVICE_NAME); HelloWorld port = ss.getHelloWorldImplPort(); //这是使用BindingProvider设置在此次访问中,对于SESSION进行保留 ((BindingProvider)port).getRequestContext().put(BindingProvider.SESSION_MAINTAIN_PROPERTY, true); //((BindingProvider)port).getRequestContext().put("thread.local.request.context", true); //测试会发现2此的打印的SESSIONID都是一样的,如果不是用以上BindingProvider的话,SESSIONID是不一样的。 //因为客户端对于WEB SERVICE访问,没特殊设置每次的访问都是新访问,服务器都会建立新的会话。 System.out.println(port.sayHi("sonx")); Thread.currentThread().sleep(3000); System.out.println(port.sayHi("sonx")); } }
3.2在多个SERVICE中保持会话。3.2.1 声明一个实现SOAPHandler接口的类:
package com.zx.cxf.handle; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import javax.xml.namespace.QName; import javax.xml.ws.handler.MessageContext; import javax.xml.ws.handler.soap.SOAPHandler; import javax.xml.ws.handler.soap.SOAPMessageContext; public class Myhandle implements SOAPHandler<SOAPMessageContext> { private List cookie = null; public Set<QName> getHeaders() { // TODO Auto-generated method stub return null; } public void close(MessageContext context) { // TODO Auto-generated method stub } public boolean handleFault(SOAPMessageContext context) { // TODO Auto-generated method stub return false; } public boolean handleMessage(SOAPMessageContext context) { try { //获得当前调用方向 Boolean out = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY); if(!out){//响应 Map<String, List<String>> responseHeaders = (Map<String, List<String>>) context.get(MessageContext.HTTP_RESPONSE_HEADERS); List<String> c = responseHeaders.get("Set-cookie"); //sessionid在该域中 if(cookie==null && c!=null){ //这是第一次HTTP调用,cookie刚刚得到 cookie = c; //保存该cookie }else{ System.out.println("DEBUG: 现在不是第一个调用HTTP了.因此COOKIE早已存在."); } }else{ //请求 //获取请求上下文 java.util.Map<String, List<String>> requestHeaders = (Map<String, List<String>>) context.get(MessageContext.HTTP_REQUEST_HEADERS); if(requestHeaders==null){ //请求上下文可能为空,构造一个新的即可 requestHeaders = new HashMap<String, List<String>>(); //将一个空的请求上下文设置到SOAPMessageContext中 context.put(MessageContext.HTTP_REQUEST_HEADERS, requestHeaders); //设置该请求上下文全局有效 context.setScope(MessageContext.HTTP_REQUEST_HEADERS, MessageContext.Scope.APPLICATION); } //如果已经获得了sessionid,将该sessionid设置到请求上下文中即可 if(cookie!=null){ requestHeaders.put("cookie", cookie); } } } catch (Exception e) { e.printStackTrace(); } return true;//返回true,意味着其它Handler也可以被调用 } }
3.2.2在客户端中调用HANDLER来完成多个SERVICE间的会话保持
package clients; import java.net.URL; import java.util.List; import javax.xml.namespace.QName; import javax.xml.ws.Binding; import javax.xml.ws.BindingProvider; import javax.xml.ws.handler.Handler; import com.zx.cxf.handle.Myhandle; import com.zx.cxf.service.HelloWorld; import com.zx.cxf.service.HelloWorld_Service; public class TestHello { private static final QName SERVICE_NAME = new QName("http://service.cxf.zx.com/", "HelloWorld"); public static final Myhandle handler = new Myhandle(); /** * @param args * @throws InterruptedException */ public static void main(String[] args) throws InterruptedException { URL wsdlURL = HelloWorld_Service.WSDL_LOCATION; HelloWorld_Service ss = new HelloWorld_Service(wsdlURL, SERVICE_NAME); HelloWorld port = ss.getHelloWorldImplPort(); //((BindingProvider)port).getRequestContext().put(BindingProvider.SESSION_MAINTAIN_PROPERTY, true); //((BindingProvider)port).getRequestContext().put("thread.local.request.context", true); //调用到了我们之前声明的HANDLER了哦。实现多个SERVICE间的会话保持。 Binding binding = ((BindingProvider) port).getBinding(); List<Handler> handlerList = binding.getHandlerChain();//获得Handler链 if (!handlerList.contains(handler)) {//防止重复插入Handler handlerList.add(handler); binding.setHandlerChain(handlerList); } System.out.println(port.sayHi("sonx")); Thread.currentThread().sleep(3000); System.out.println(port.sayHi("sonx")); } }
四。笔者后言
以上多为实现为主,注释也加上了。当然中间一些理论性的解释并没有,因此这里给读者一个传送门。
这是笔者拜读后完成的。两两结合,大家花点时间,应该可以明白的了。CXF使用JAX-WS,我不知道AXIS2的原理是什么,感觉封装性比CXF更强。
不过灵活性感觉没CXF强。想独立把AXIS2拿出来结合到已有的WEB项目中,也很麻烦。可能是我没弄清楚吧。
传送门:http://blog.csdn.net/ts1990421/article/details/6804933
对了,以上的客户端代码只是对同一个SERVICE进行测试,不作得准。笔者已经写好了2个SERVICE的测试了代码了。迟点贴上。