1. WebService概述
1.1 WebService定义
W3C组织对其的定义如下,它是一个软件系统,为了支持跨网络的机器间相互操作交互而设计。Web Service服务通常被定义为一组模块化的API,它们可以通过网络进行调用,来执行远程系统的请求服务。
简单的说:WebService即Web服务,它是一种跨编程语言和跨操作系统平台的远程调用技术,是接口技术的一种。
Web服务:基于HTTP和XML的技术,HTTP是互联网上应用最为广泛的一种网络协议,而XML是跨平台的基础。
跨编程语言和跨操作平台:假设服务端程序采用java编写,客户端程序则可以采用其他编程语言编写,反之亦然!跨操作系统平台则是指服务端程序和客户端程序可以在不同的操作系统上运行。
远程调用:就是一台计算机a上的一个程序可以调用到另外一台计算机b上的一个对象的方法,譬如,银联提供给商场的pos刷卡系统,商场的POS机转账调用的转账方法的代码其实是跑在银行服务器上。再比如,amazon,天气预报系统,淘宝网,校内网,百度等把自己的系统服务以webservice服务的形式暴露出来,让第三方网站和程序可以调用这些服务功能。
很多个系统,进行分布的部署,分布的系统数据通信 解决技术就是WebService。
1.2 其它概念
1.2.1 客户端和服务端
WebService开发可以分为服务器端开发和客户端开发两个方面:
- 服务端(服务提供者)开发:把公司内部系统的业务方法发布成WebService服务,供远程合作单位和个人调用。
- 客户端(服务请求者)开发:调用别人发布的WebService服务,大多数人从事的开发都属于这个方面,例如,调用天气预报WebService服务。
1.2.2 接口的概念
简单的说就是API规范,可以进行不同系统间交互。
可插拔:对于软件来说,是一个接耦合的思想。
1.2.3 Web Service 分类
Web Service也分为两大类:
- 一类是基于Soap协议的WebService。
- 一类是基于REST风格的Web Service。
2. Soap方式的WebService
2.1 概述
2.1.1 JAX-WS
JAX-WS(Java API for XML Web Services)规范是一组XML web services的JAVA API。
现行的最新的规范版本为JAX-WS2.0 (JSR 224)。
JAX-WS主要是支持和实现Soap方式的WebService。
2.1.2 Soap方式WebService的来历
SOAP方式的WebService最早由微软提出和实现,是W3C的标准之一。在跨系统和跨平台的系统通信中,其以平台无关性,获得了广泛的应用,他为整个企业甚至多个组织之间的业务流程的集成提供了一个通用机制。
它使用开放的XML(标准通用标记语言下的一个子集)标准来描述、发布、发现、协调和配置这些应用程序,用于开发分布式的互操作的应用程序。XML是该技术跨平台的基础。由于XML与平台、编程语言无关,因此,谁都能读写。
2.2 CXF框架
2.2.1 CXF框架的概念
CXF封装了JAX-WS。jax-ws是sun公司规范,但也给了实现,因此也可以直接使用JAX-WS开发WebService程序,但一般实际开发不用。
CXF 大大简化了 Services 的创建,同时它继承了 XFire 传统,一样可以天然地和 Spring 进行无缝集成。
Apache CXF = Celtix + XFire,Apache CXF 的前身叫 Apache CeltiXfire,现在已经正式更名为 Apache CXF 了,以下简称为 CXF。CXF 继承了 Celtix 和 XFire 两大开源项目的精华,提供了对 JAX-WS 全面的支持。
CXF WebService 开发,主要分为两种服务提供方式 JAX-WS 、JAX-RS 。
- JAX-WS 传输数据,就是XML格式,基于SOAP协议 。
- JAX-RS 传输数据,传输XML格式或者JSON格式,基于HTTP协议 。
2.2.2 安装
先解压到一个目录(不能有中文、特殊字符)
配置环境变量
CXF_HOME=D:\DevIDE\Java\apache-cxf-2.6.15
path=%CXF_HOME%/bin;测试是否安装成功 命令:wsdl2java -v
3. CXF的快速入门
3.1 CXF服务端开发
官方案例:
<!--使用maven导入jar包--> <properties> <cxf.version>${project.version}</cxf.version> </properties> <dependencies> <!-- jax-ws前端控制模块(处理发过来的服务业务) --> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-frontend-jaxws</artifactId> <version>3.1.9</version> </dependency> <!-- cxf的数据传输模块 --> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-transports-http</artifactId> <version>3.1.9</version> </dependency> <!-- Jetty is needed if you're using the CXFServlet --> <!-- 用来启动一个http服务用的(Jetty-tomcat)jetty,是cxf默认使用的http服务的容器 切记:如果你web运行使用是tomcat容器的话,那么你必须不能引入下面的jar --> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-transports-http-jetty</artifactId> <version>3.1.9</version> </dependency> <!-- 引入日志slf4j(自己引入,官方未提供) --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>${slf4j.version}</version> </dependency> </dependencies>
添加log4j的核心配置文件。
开发:三大步
第一步:编写SEI
SEI:jax-ws规范中,Service EndPoint InterFace服务端点接口
对于java的api来说,就是一组接口和实现类
接口里面的方法就是要暴露(expose)出去给客户端调用的
//SEI接口 public interface HelloWorld { String sayHi(@WebParam(name = "text") String text); } //SEI实现 public class HelloWorldImpl implements HelloWorld { public String sayHi(String text) { return "Hello " + text; } }
第二步:在SEI的接口类上添加注解
可能找不到该注解,原因:该注解是jdk6版本的规范中,低于该版本的jdk没有。
@WebService public interface HelloWorld { String sayHi(@WebParam(name = "text") String text); }
第三步:发布服务
public class Server { protected Server() throws Exception { System.out.println("Starting Server"); HelloWorldImpl implementor = new HelloWorldImpl(); //new实现类 JaxWsServerFactoryBean svrFactory = new JaxWsServerFactoryBean(); //1.服务端工厂类 //2.设置了三个属性 //2.1设置SEI接口class svrFactory.setServiceClass(HelloWorld.class); //2.2服务地址(给客户端调用):协议+ip+端口+项目名字(服务名字) svrFactory.setAddress("http://localhost:9000/helloWorld"); //2.3设置SEI的一个实现者(对象) svrFactory.setServiceBean(implementor); //添加输入日志 svrFactory.getInInterceptors().add(new LoggingInInterceptor()); //添加输出日志 svrFactory.getOutInterceptors().add(new LoggingOutInterceptor()); //3.创建并发布服务 //底层,默认会发起一个http服务,默认使用jetty svrFactory.create(); } public static void main(String args[]) throws Exception { new Server(); System.out.println("Server ready..."); Thread.sleep(5 * 60 * 1000); System.out.println("Server exiting"); System.exit(0); } }
测试是否服务发布成功(通过访问wsdl):127.0.0.1:8888/helloWorld(服务名)?wsdl
如果用火狐访问,头部信息隐藏。其他浏览器没有问题。
http://127.0.0.1:8888/helloWorld :真正的webservice服务
http://127.0.0.1:8888/helloWorld?wsdl :wsdl文档,客户端和服务端直接的桥梁
jetty就是一个应用服务器,和tomcat作用差不多。但它更轻量级。可以用于内嵌的web服务、可以方便测试。
cxf开发的时候,sei上面的注解都写在接口上。
wsdl文档的生成更依赖于接口,和实现类关系不大,好处是解耦合,更加面向对象。
3.2 CXF客户端开发
官方案例:
public final class Client { private Client() { } public static void main(String args[]) throws Exception { //1.代理桩工厂 JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean(); //添加输入日志 factory.getInInterceptors().add(new LoggingInInterceptor()); //添加输出日子 factory.getOutInterceptors().add(new LoggingOutInterceptor()); //2.设置WebService地址 factory.setAddress("http://localhost:9000/helloWorld"); //3.根据工厂创建本地代理对象(桩对象) HelloWorld client = factory.create(HelloWorld.class); System.out.println(client.sayHi("World")); } }
3.2.1 生成桩(stub)、本地代理(proxy)接口类
生成桩(stub)、本地代理(proxy)接口类—wsdl2java
-p :指定其wsdl的命名空间,要生成代码的包名
-d :指定要产生代码的目录
wsdl2java -d . p cn.demo.hw.client.stub http:127.0.0.1:9000/helloWorld?wsdl
3.3. 日志拦截器
拦截器:增强功能!CXF拦截器:增强扩展CXF的!
日志拦截器的作用:它是cxf拦截器的一种,查看日志(soap相关)。可以解决:相互调用的日志、发送或接收的报文、请求的次数等。日志拦截器中的ID,是拦截的编号,作用是为了标识请求的次数以及请求和响应对应的编号。
输入拦截器,InInterceptors:在方法进入前执行。
输出拦截器,OutInterceptors:在方法进入后执行。
CXF已经实现了很多种拦截器,如果要自定义拦截器,则需要实现PhaseInterceptor接口(超级接口Interceptor),不过一般都是继承自AbstractPhaseInterceptor< T extends org.apache.cxf.message.Message >类,这个类可以指定继承它的拦截器在什么阶段被启用,阶段属性可以通过org.apache.cxf.phase.Phase 中的常量指定值。
3.4.运行机制
3.4.1 WebService三要素
SOAP (Simple Object Access Protocol):简易对象访问协议,soap用来描述传递信息的格式。
WSDL (WebServices Description Language):Web服务描述语言,用来描述如何访问具体的接口。
UDDI (Universal Description Discovery and Integration):通用描述、发现及整合,用来管理、分发、查询webService。现在弱化uddi,原因:现在的接口交互,一般服务方和调用方直接商量。
3.4.2 原理
3.5. 注解- JAX-WS规范
3.5.1 注解的作用
只要使用了注解,那么服务端的代码怎么改其实都对wsdl或客户端没有影响。
好处:屏蔽了服务端的代码的具体内容。
3.5.2.常见ws注解的分类和使用
类上面的注解
方法上面的注解
3.5.3 Webservice的方法的编写要求和方法隐藏
SEI的方法,必须是公有,非静态。
当wsdl发生变化后,客户端的桩的代码也需要重新生成。
4. SOAP方式的CXF(JAX-WS)与Spring的整合
服务端的整合方式:
发挥spring的粘合剂的作用,将对象创建的权利和管理都交给spring。(IoC思想)
可以将webservice的服务和普通的web服务放在一起启动。换句话说,我们要使用tomcat来启动ws服务。不再使用main方法。
整体:web容器tomcat—》加载spring容器—-》初始化webservice服务。
参考官方demo :java_first_spring_support
4.1 服务端开发
4.1.1 新建maven的web工程
4.1.2 导入jar搭建基本开发环境
<properties> <!-- 全局编码设置为u8 --> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <spring.version>3.2.12.RELEASE</spring.version> <cxf.version>3.1.9</cxf.version> <slf4j.version>1.7.21</slf4j.version> <junit.version>4.11</junit.version> <oracle.version>10.2.0.4.0</oracle.version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>${slf4j.version}</version> </dependency> <dependency> <groupId>com.oracle</groupId> <artifactId>ojdbc14</artifactId> <version>${oracle.version}</version> <scope>runtime</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <!-- jax-ws前端控制模块(处理发过来的服务业务) --> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-frontend-jaxws</artifactId> <version>${cxf.version}</version> </dependency> <!-- cxf的数据传输模块 --> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-transports-http</artifactId> <version>${cxf.version}</version> </dependency> </dependencies> <build> <plugins> <!-- 编译的jdk版本 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>1.7</source> <target>1.7</target> </configuration> </plugin> <!-- tomcat插件:内置默认:运行:tomcat:run --> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>tomcat-maven-plugin</artifactId> <version>1.1</version> <configuration> <port>8888</port> </configuration> </plugin> </plugins> </build>
4.1.3 SEI的编写,并在接口类上加注解—业务层
//SEI接口 @WebService( name="MobileAddressService",//porttype serviceName="MobileService"//服务的名字 ,portName="MobilePort"//port ,targetNamespace="http://ws.mobile.cn/"//自定义名称空间 ) @BindingType(javax.xml.ws.soap.SOAPBinding.SOAP12HTTP_BINDING) public interface MobileAddressService { /** * * 说明:根据手机号码查询归属地(暴露出去方法) * @param mobileNo * @return */ @WebMethod(operationName="getAddressByMobileNo") public@WebResult(name="address") String getAddressByMobileNo(@WebParam(name="mobileNo")String mobileNo); } //sei的实现 public class MobileAddressServiceImpl implements MobileAddressService{ //注入dao private MobileAddressDAO mobileAddressDAO; public void setMobileAddressDAO(MobileAddressDAO mobileAddressDAO) { this.mobileAddressDAO = mobileAddressDAO; } @Override public String getAddressByMobileNo(String mobileNo) { //基本判断 //如果为空,则直接返回 if(StringUtils.isEmpty(mobileNo)){ return null; }else{ //调用dao查询 //手机号码只需要匹配前七位 return mobileAddressDAO.getAddressByMobileNo(mobileNo.substring(0, 7)); } } }
4.1.4 持久层
引入jdbc工具类
//dao接口 public interface MobileAddressDAO { /** * 根据手机号码获取归属地 * @param mobileNo * @return */ public String getAddressByMobileNo(String mobileNo); } //dao实现 //jdbc public class MobileAddressDAOImpl implements MobileAddressDAO{ @Override public String getAddressByMobileNo(String mobileNo) { Connection conn=null; PreparedStatement pstmt=null; ResultSet rs=null; try { //获取连接 conn = JDBCUtils.getConnection(); //获取pstmt pstmt = conn.prepareStatement("select * from t_mobileaddress where mobileNo=?"); //设置参数 pstmt.setString(1, mobileNo); //执行查询,获取rs rs = pstmt.executeQuery(); //获取归属地信息 String address=null; if(rs.next()){ address = rs.getString("address"); } return address; } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("服务端查询手机号归属地业务失败",e); }finally { JDBCUtils.release(conn, pstmt, rs); } } }
4.1.5 配置web.xml
web容器启动的时候自动加载、初始化cxf的服务。
<!-- spring配置文件位置 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <!-- spring核心监听器 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 配置cxf的前端控制器 分发服务请求 --> <servlet> <servlet-name>cxf</servlet-name> <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>cxf</servlet-name> <url-pattern>/services/*</url-pattern> </servlet-mapping>
4.1.6 配置spring整合cxf的配置文件
<?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" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd"> <!-- 初始化cxf servlet --> <import resource="classpath:META-INF/cxf/cxf.xml" /> <import resource="classpath:META-INF/cxf/cxf-servlet.xml" /> <!-- 配置webservice服务 jaxws:server:服务工厂+bean整合体 三个属性: 1.address:发布地址,服务地址 serviceClass:接口类型 --> <jaxws:server address="/mobile" serviceClass="cn.itcast.cxf.service.MobileAddressService"> <!-- 实现对象 --> <jaxws:serviceBean> <ref bean="mobileAddressService"/> </jaxws:serviceBean> <!-- 拦截器 --> <jaxws:inInterceptors> <ref bean="loggingInInterceptor"/> </jaxws:inInterceptors> <jaxws:outInterceptors> <ref bean="loggingOutInterceptor"/> </jaxws:outInterceptors> </jaxws:server> <!-- 日志拦截器bean --> <bean id="loggingInInterceptor" class="org.apache.cxf.interceptor.LoggingInInterceptor"></bean> <bean id="loggingOutInterceptor" class="org.apache.cxf.interceptor.LoggingOutInterceptor"></bean> <!-- 构建service的bean --> <bean id="mobileAddressService" class="cn.itcast.cxf.service.impl.MobileAddressServiceImpl"> <property name="mobileAddressDAO" ref="mobileAddressDAO"/> </bean> <!-- dao --> <bean id="mobileAddressDAO" class="cn.itcast.cxf.dao.impl.MobileAddressDAOImpl"></bean> </beans>
测试:
访问地址:Web容器的地址+servletmap地址+服务地址
4.2 客户端开发
目标:客户端获取桩(本地代理—spring自动提供)对象就ok
如果和spring整合后,直接从spring容器中获取就可以了。(原理:cxf整合进了spring了)
4.2.1 新建maven的web工程
4.2.2 导入jar搭建基本开发环境
引入jar和服务端一样
pom.xml:
<properties> <!-- 全局编码设置为u8 --> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <spring.version>3.2.12.RELEASE</spring.version> <cxf.version>3.1.9</cxf.version> <slf4j.version>1.7.21</slf4j.version> <junit.version>4.11</junit.version> <servlet.version>2.5</servlet.version> <jsp.version>2.0</jsp.version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>${slf4j.version}</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <!-- jax-ws前端控制模块(处理发过来的服务业务) --> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-frontend-jaxws</artifactId> <version>${cxf.version}</version> </dependency> <!-- cxf的数据传输模块 --> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-transports-http</artifactId> <version>${cxf.version}</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>${servlet.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jsp-api</artifactId> <version>${jsp.version}</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <!-- 编译的jdk版本 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>1.7</source> <target>1.7</target> </configuration> </plugin> <!-- tomcat插件:内置默认:运行:tomcat:run --> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>tomcat-maven-plugin</artifactId> <version>1.1</version> <configuration> <port>9999</port> </configuration> </plugin> </plugins> </build>
4.2.3 jsp页面
略
4.2.4 写servlet
public class MobileServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1。获取请求参数 request.setCharacterEncoding("utf-8"); String mobileNo = request.getParameter("mobileNo"); //2.调用service查询 //先获取spring容器对象 ApplicationContext ac = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); //获取service MobileService mobileService=(MobileService) ac.getBean("mobileService"); //调用方法 String address = mobileService.getAddressByMobileNo(mobileNo); //3.返回结果到响应 String result="手机号"+mobileNo+"没有归属地信息"; if(!StringUtils.isEmpty(address)){ result="手机号"+mobileNo+"的归属地信息是:"+address; } response.setContentType("text/html; charset=UTF-8"); response.getWriter().print(result); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
4.2.5 编写业务层,注入桩(本地代理对象)
esdl2java命令生成桩。
public interface MobileService { public String getAddressByMobileNo(String mobileNo); } public class MobileServiceImpl implements MobileService{ //直接注入桩(本地代理对象) private MobileAddressService mobileAddressService; public void setMobileAddressService(MobileAddressService mobileAddressService) { this.mobileAddressService = mobileAddressService; } @Override //远程调用 public String getAddressByMobileNo(String mobileNo) { return mobileAddressService.getAddressByMobileNo(mobileNo); } }
4.2.6 配置Spring核心配置文件,完成整合(桩的生命周期交给spring管理)
<?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" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd"> <!-- 本地代理对象:spring整合了cxf的客户端(提供了桩,本地代理对象)--> <!-- jaxws:client:相当于jaxwsproxyfactory address:客户端访问服务端的ws服务地址 serviceClass:本地代理对象类型 id:桩对象(本地代理对象) --> <jaxws:client id="mobileAddressService" address="http://localhost:8888/cxf_jaxws_spring_server/services/mobile" serviceClass="cn.itcast.cxf.stub.MobileAddressService"> <!-- 拦截器 --> <jaxws:inInterceptors> <ref bean="loggingInInterceptor"/> </jaxws:inInterceptors> <jaxws:outInterceptors> <ref bean="loggingOutInterceptor"/> </jaxws:outInterceptors> </jaxws:client> <!-- 日志拦截器bean --> <bean id="loggingInInterceptor" class="org.apache.cxf.interceptor.LoggingInInterceptor"></bean> <bean id="loggingOutInterceptor" class="org.apache.cxf.interceptor.LoggingOutInterceptor"></bean> <!-- service --> <bean id="mobileService" class="cn.itcast.cxf.service.impl.MobileServiceImpl"> <!-- 注入桩 --> <property name="mobileAddressService" ref="mobileAddressService"/> </bean> </beans>
转载自:http://blog.csdn.net/shuaicihai/article/details/56036007