Webservice与CXF框架快速入门
1. Webservice
Webservice是一套远程调用技术规范
远程调用RPC, 实现了系统与系统进程间的远程通信.
java领域有很多可实现远程通讯的技术,如:RMI(Socket + 序列化)、Binary-RPC(Http+二进制, 代表Hessian)、XML-RPC(Http+XML, 代表Burlap, WebService用的SOAP)、JMS(使用消息机制)、Mina(使用NIO)等, 底层都是基于http/socket和网络IO来实现的.
从效率上来讲, RMI > Hessian >> Burlap >> web service.
构成webservice的几个要素:
1.WSDL:web服务描述语言. 即webservice服务的使用说明书, 自动生成,无需编写
通过访问类似http://127.0.0.1:12345/weather?wsdl的地址可以查看
它长如下这样子, 阅读顺序从下往上
2.SOAP:简单对象访问协议 http post + xml
必有 envelope 标签,将XML文档标识为一条 SOAP 消息
必有 body 标签,传输的信息
可选 header 标签,包含头部信息
可选 fault 标签,提供有关在处理此消息所发生错误的信息
SOAP常用有1.1, 1.2两个版本. jdk的Jaxws只支持发布SOAP1.1服务.
如要SOAP1.2服务, 需要引入jaxws-ri, 并在实现类上加入注解@BindingType(SOAPBinding.SOAP12HTTP_BINDING)
3.UDDI:提供webservice服务的注册和搜索功能, 不实用
服务端
public interface WeatherInterface { public String queryWeather(String cityName); }
// 实现类前加WebService注解
//@BindingType(SOAPBinding.SOAP12HTTP_BINDING) @WebService public class WeatherImpl implements WeatherInterface { @Override public String queryWeather(String cityName) { String weather = "晴"; return weather; } }
public class WeatherServer { public static void main(String[] args) { //Endpoint发布服务, 参数1: 服务地址, 参数2: 服务实现类 Endpoint.publish("http://127.0.0.1:12345/weather", new WeatherImpl()); } }
对于定义的服务实现类, 可以用注解进行修饰
@WebService 定义服务,在类上边
targetNamespace:指定命名空间
name:portType的名称
portName:port的名称
serviceName:服务名称
endpointInterface:如果一个服务类实现了多个服务接口,但只需要发布一个接口的方法,可通过此注解指定要发布服务的接口
@WebMethod 定义方法,在方法上边
operationName:方法名
exclude:设置为true表示此方法不是webservice方法,不会发布,默认是false
@WebResult 定义返回值,在方法返回值前边
name:返回结果值的名称
@WebParam 定义参数,在方法参数前边
name:指定参数的名称
客户端
首先, 用命令wsimport -s . http://127.0.0.1:12345/weather?wsdl生成支持类, 导入工程中
第一种使用方式 使用相关类
public class Client1 { public static void main(String[] args) throws IOException { // WSDL的地址, 非服务地址 URL url = new URL("http://127.0.0.1:12345/weather?wsdl"); //创建服务名称 //参数一: namespaceURI – WSDL文档中 types/targetNamespace //参数二: localPart - 服务视图名, WSDL文档中 service-name QName qname = new QName("http://WebXml.com.cn/", "WeatherWS"); //创建服务视图 //参数1: wsdlDocumentLocation WSDL地址 //参数2: serviceName 服务名称 Service service = Service.create(url, qname); //获取服务类 getPort(WSDL文档中portType-name) WeatherWSSoap weatherWSSoap = service.getPort(WeatherWSSoap.class); //调用方法 WSDL文档中portType-operation-name String result = weatherWSSoap.queryWeather("北京"); System.out.println(result); } }
第二种使用方式 http工具访问
public class Client2 { public static void main(String[] args) throws IOException { // 服务地址 URL url = new URL("http://127.0.0.1:12345/weather"); // 创建连接对象 HttpURLConnection connection = (HttpURLConnection) url.openConnection(); // 设置参数 // Http发送方式:POST必须大写 connection.setRequestMethod("POST"); // content-type connection.setRequestProperty("content-type", "text/xml;charset=utf-8"); // 设置输入输出,默认connection没有读写权限, connection.setDoInput(true); connection.setDoOutput(true); // 发送请求 String soapXML = getXML("北京"); OutputStream os = connection.getOutputStream(); os.write(soapXML.getBytes()); // 接收响应 int responseCode = connection.getResponseCode(); if(200 == responseCode){ InputStream is = connection.getInputStream(); InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); StringBuilder sb = new StringBuilder(); String temp = null; while(null != (temp = br.readLine())){ sb.append(temp); } System.out.println(sb.toString()); is.close(); isr.close(); br.close(); } os.close(); } // 组织SOAP数据 public static String getXML(String cityName){ String soapXML = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +"<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">" +"<soap:Body>" +"<getWeatherInfo xmlns=\"http://WebXml.com.cn/\">" +"<cityName>"+cityName+"</cityName>" +"<userID></userID>" +"</getWeatherInfo>" +"</soap:Body>" +"</soap:Envelope>"; return soapXML; } }
第三种使用方式 浏览器访问
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <script type="text/javascript"> function queryWeather() { var xhr = new XMLHttpRequest(); xhr.open("post", "http://127.0.0.1:12345/weather", true); xhr.setRequestHeader("content-type","text/xml;charset=utf-8"); //设置回调函数 xhr.onreadystatechange=function(){ if(4 == xhr.readyState && 200 == xhr.status){ alert(xhr.responseText); } } //组织SOAP协议数据 var soapXML = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +"<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">" +"<soap:Body>" +"<getWeatherInfo xmlns=\"http://WebXml.com.cn/\">" +"<cityName>"+document.getElementById("cityName").value+"</cityName>" +"<userID></userID>" +"</getWeatherInfo>" +"</soap:Body>" +"</soap:Envelope>"; //发送数据 xhr.send(soapXML); } </script> </head> <body> 天气查询:<input type="text" id="cityName"/> <input type="button" value="查询" onclick="javascript:queryWeather();"/> </body> </html>
2. CXF框架
CXF是一个开源的webservice框架
CXF支持SOAP1.1/1.2,REST 协议
CXF支持XML,JSON(仅REST下) 的数据格式
服务端 JAX-WS方式(SOAP)
//@BindingType(SOAPBinding.SOAP12HTTP_BINDING) 默认发布SOAP1.1, 该注解发布SOAP1.2 @WebService public interface WeatherInterface { public String queryWeather(String cityName); }
public class WeatherImpl implements WeatherInterface { @Override public String queryWeather(String cityName) { String weather = "晴"; return weather; } }
//与spring整合后可不要该类 public class WeatherServer { public static void main(String[] args) { JaxWsServerFactoryBean jaxWsServerFactoryBean = new JaxWsServerFactoryBean(); //设置服务接口 jaxWsServerFactoryBean.setServiceClass(WeatherInterface.class); //设置服务实现类 jaxWsServerFactoryBean.setServiceBean(new WeatherImpl()); //设置服务地址 jaxWsServerFactoryBean.setAddress("http://127.0.0.1:12345/ws/weather"); //发布 jaxWsServerFactoryBean.create(); } }
与web整合, spring+CXFservlet替代WeatherServer类
<?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" xmlns:cxf="http://cxf.apache.org/core" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd"> <!-- 服务实现类 --> <bean id ="weatherImpl" class="com.zx.server.WeatherImpl"/> <!-- 拦截器 -- <bean id ="inIntercepter" class="org.apache.cxf.interceptor.LoggingInInterceptor"/> <bean id ="outIntercepter" class="org.apache.cxf.interceptor.LoggingOutInterceptor"/> --> <!-- 方式1 用JaxWsServerFactoryBean发布SOAP协议的服务 --> <jaxws:server address="/weather" serviceClass="com.zx.server.WeatherInterface"> <jaxws:serviceBean> <ref bean="weatherImpl"/> <!-- 实现类 --> </jaxws:serviceBean> <!-- 配置拦截器 -- <jaxws:inInterceptors> <ref bean="inIntercepter"/> </jaxws:inInterceptors> <jaxws:outInterceptors> <ref bean="outIntercepter"/> </jaxws:outInterceptors> --> </jaxws:server> <!-- 方式2 用Endpoint发布SOAP协议的服务 --> <jaxws:endpoint address="/weather" implementor="com.zx.server.WeatherImpl"/> </beans>
endPoint 只支持发布实现类
JaxWsServerFactoryBean还可以发布接口
web.xml
<!-- 配置CXF的Servlet --> <servlet> <servlet-name>CXF</servlet-name> <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>CXF</servlet-name> <url-pattern>/ws/*</url-pattern> </servlet-mapping>
客户端
首先, 类似wsimport, 使用CXF下的Wsdl2java生成支持类, 导入工程中
public class WeatherClient { public static void main(String[] args) { JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean(); //设置服务接口 jaxWsProxyFactoryBean.setServiceClass(WeatherInterface.class); //设置服务地址 jaxWsProxyFactoryBean.setAddress("http://127.0.0.1:12345/ws/weather"); //获取服务接口, 可与spring整合替代 WeatherInterface weatherInterface = jaxWsProxyFactoryBean.create(WeatherInterface.class); //调用查询方法 String weather = weatherInterface.queryWeather("保定"); System.out.println(weather); } }
与spring整合后由spring生成weatherInterface实例
<!-- 用JaxWsProxyFactoryBean建立客户端 --> <jaxws:client id="weatherClient" address="http://127.0.0.1:12345/ws/weather" serviceClass="com.zx.WeatherInterface"/>
服务端 JAX-RS方式(REST风格)
基础bean
@XmlRootElement(name="student") //能被格式化为XML public class Student { private long id; private String name; private Date birthday; public getXXX(), setXXX(); // get set方法 }
服务接口
@WebService @Path("/student") //将请求路径“/student”映射到接口上 public interface StudentInterface { @POST // 指定请求方式 GET / POST @Produces(MediaType.APPLICATION_XML) //指定服务数据类型 XML / JSON @Path("/query/{id}") //将请求路径/query映射到方法上, 参数注入配合@PathParam注解 public Student query(@PathParam("id")long id); @GET @Produces({"application/json;charset=utf-8",MediaType.APPLICATION_XML}) //同时指定json和xml, 添加访问参数?_type=json返回json; 添加?_type=xml返回XML @Path("/queryList/{name}") public List<Student> queryList(@PathParam("name")String name); }
服务实现, 仅举例
public class StudentImpl implements StudentInterface { @Override public Student query(long id) { Student st = new Student(); st.setId(110); st.setName("张三"); st.setBirthday(new Date()); return st; } @Override public List<Student> queryList(String name) { Student st = new Student(); st.setId(110); st.setName("张三"); st.setBirthday(new Date()); List<Student> list = new ArrayList<Student>(); list.add(st); return list; } }
发布
public class StudentServer { public static void main(String[] args) { //JAXRSServerFactoryBean 发布REST的服务 JAXRSServerFactoryBean jaxRSServerFactoryBean = new JAXRSServerFactoryBean(); jaxRSServerFactoryBean.setServiceBean(new StudentImpl()); jaxRSServerFactoryBean.setResourceClasses(StudentImpl.class); jaxRSServerFactoryBean.setAddress("http://127.0.0.1:12345/ws/user"); jaxRSServerFactoryBean.create(); } }
与web整合, spring+CXFservlet替代StudentServer类
<?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" xmlns:cxf="http://cxf.apache.org/core" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd"> <!-- 配置服务实现类 --> <bean id = "studentImpl" class="com.zx.StudentImpl"/> <!-- 使用JAXRSServerFactoryBean发布REST的服务 --> <jaxrs:server address="/user"> <jaxrs:serviceBeans> <ref bean="studentImpl"/> </jaxrs:serviceBeans> </jaxrs:server> </beans>
web.xml
<!-- 配置CXF的Servlet --> <servlet> <servlet-name>CXF</servlet-name> <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>CXF</servlet-name> <url-pattern>/ws/*</url-pattern> </servlet-mapping>
REST风格下WSDL访问网址为http://127.0.0.1:12345/ws/user?_wadl