商业性web项目
学习WebService开始之前,让我们先来了解一下一般的商业性项目结构。
- 首先用户在浏览器上输入网址,显示相应页面。
- 在视图层根据不同操作访问对应Controler =》Service =》Dao
- 由Dao层访问数据库,获取相应数据,并返回。
- 视图接收到返回的数据,对页面进行渲染,显示到浏览器。
- 项目中的部分接口,可以作为服务发布出去,由第三方应用访问。
- 而该项目也调用到第三方服务,获取相关的数据。
上述的1-4步是普通的请求与响应,而5、6步需要与第三方平台交互,而我们无法控制第三方所使用的语言,所以要完成不同语言平台的相互访问,就要使用到WebService了。
Socket实现客户端与服务端通信
在学习WebService之前,我们回顾一下Socket是如何通信的,下面是一个简单的案例。
package socket; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; /** * Socket服务端:将客户端发送的数据转换为大写返回 */ public class MySocketServer { public static void main(String[] args) throws Exception { ServerSocket ss = new ServerSocket(8080); System.out.println("服务地址为:127.0.0.1:8080"); Socket socket = ss.accept(); // 接收客户端数据 InputStream in = socket.getInputStream(); byte[] buffer = new byte[1024]; int len = -1; len = in.read(buffer); String data = new String(buffer, 0, len); System.out.println("接收客户端数据:" + data); // 返回数据 OutputStream out = socket.getOutputStream(); String upperData = data.toUpperCase(); out.write(upperData.getBytes("UTF-8")); // 释放资源 out.flush(); out.close(); in.close(); socket.close(); ss.close(); } }
package socket; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.util.Scanner; /** * Socket客户端 */ public class MySocketClient { public static void main(String[] args) throws Exception { Scanner sc = new Scanner(System.in); System.out.println("请输入字符串:"); String data = sc.nextLine(); // 定位服务地址 Socket socket = new Socket("127.0.0.1", 8080); // 发送数据 OutputStream out = socket.getOutputStream(); out.write(data.getBytes()); // 接收服务端返回数据 InputStream in = socket.getInputStream(); byte[] buffer = new byte[1024]; int len = -1; len = in.read(buffer); String result = new String(buffer, 0, len); System.out.println("服务端返回数据:" + result); // 释放资源 in.close(); out.close(); socket.close(); } }
WebService介绍
什么是WebService?
- 是应用程序组件,可以通过web发布服务。
- 使用开放协议进行通信,如:Http
- 是独立的,可自我描述(WSDL描述语言)
- 通过使用UDDI来发现
- 服务发布后可被其他程序所使用
- XML是WebService的基础
- 可向全世界发布功能和消息
通俗的讲,WebService就是一个部署在Web容器上的一个应用程序,它向外界暴露一个能够通过Web进行调用的API。你能够通过编程的方法
来调用这个应用程序,我们把调用WebService的应用程序叫做客户端,发布这个Web服务的机器叫做服务器。
WebService元素
- SOAP( simple object access protocol ):简单对象访问协议
- UDDI:通用发现、描述、整合
- WSDL:webservice 描述语言
什么是SOAP?
SOAP,简单对象访问协议,可实现应用程序间的通信,是一种用于发送消息的数据格式,独立于平台,独立于语言,基于XML的一种通信协议。
什么是WSDL?
可以理解为WebService的说明书,使用XML编写,用来描述WebService以及如何访问WebService的语言。
Get请求
这里有一个发布第三方服务的网站:webxml.com.cn/zh_cn/index.aspx,我们可以使用get请求一下“手机号码归属地查询”服务。
import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; /** * 调用第三方服务,获取手机号归属地测试 */ public class MobileTest { public static void getMobileCodeInfo(String mobileCode, String userID) throws Exception{ String urlStr = "http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx/getMobileCodeInfo?" + "mobileCode="+ mobileCode +"&userID=" + userID; URL url = new URL(urlStr); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(5000); conn.setRequestMethod("GET"); if(conn.getResponseCode() == HttpURLConnection.HTTP_OK){ InputStream in = conn.getInputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len = -1; while ((len = in.read(buffer)) != -1){ baos.write(buffer, 0, len); } System.out.println(baos.toString()); } } public static void main(String[] args) throws Exception{ getMobileCodeInfo("18305422179", ""); } }
执行结果:
Soap请求
还是以刚刚的“手机号码归属地查询”服务为例,使用soap进行请求。
我们点开服务说明,查看WSDL信息。
获取到WSDL地址,可以通过java的wsimport命令生成服务代码。切换到指定目录下,执行如下命令:
wsimport http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx?WSDL
wsimport命令有几个选项,可以简单了解下:
-d 指定输出文件的位置
-s 解析java源码,默认只解析class源码
-p 指定包名
这时可以看到,目录下了几个.class文件。
将这些文件打成jar包,引用到自己的项目中去。
将我们生成好的jar包添加到项目中,然后测试soap请求,代码如下:
import cn.com.webxml.MobileCodeWS; import cn.com.webxml.MobileCodeWSSoap; public class TestPhone { public static void main(String[] args) { MobileCodeWS ws = new MobileCodeWS(); MobileCodeWSSoap soap = ws.getMobileCodeWSSoap(); String result = soap.getMobileCodeInfo("18305422179", ""); System.out.println(result); } }
执行结果:
使用wsimport生成本地代理
我们知道了怎么样去调用别人发布的服务,但我们如何自己发布一个WebService服务呢?
下面使用wsimport生成本地代理,发布服务,并测试。
首先,创建一个项目,定义一个WebService服务类。
import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebResult; import javax.jws.WebService; import javax.xml.ws.Endpoint; @WebService(serviceName = "HobbyServiceService") // 定义webservice服务类 public class HobbyService { @WebMethod(operationName = "getHobbyInfo") public @WebResult(name = "hobby") Hobby getHobbyInfo(@WebParam(name = "code") int code){ Hobby hobby = new Hobby(); hobby.setCode(code); switch (code){ case 1: hobby.setName("游戏"); break; case 2: hobby.setName("唱歌"); break; case 3: hobby.setName("篮球"); break; default: hobby.setName("无爱好"); } return hobby; } public static void main(String[] args) { String address = "http://localhost:9999/ws/hobby"; HobbyService hs = new HobbyService(); Endpoint.publish(address, hs); System.out.println("发布服务地址为:" + address + "?WSDL"); } }
将我们的地址发布后,可以在浏览器*问:
此时服务已经发布成功,使用wsimport命令生成服务代码,然后新建一个项目,编写客户端代码,测试本地服务
public class HobbyClient { public static void main(String[] args) {
// 创建服务类 HobbyServiceService hss = new HobbyServiceService(); HobbyService port = hss.getHobbyServicePort(); Hobby info = port.getHobbyInfo(1); System.out.println(info); } }
执行结果:
WSDL文件介绍
阅读WSDL文件建议从下往上,这样更容易理解,以下是一部分参数说明:
service name 服务名
port name 服务访问方式
soap address 服务地址
binding name 绑定名
binding type webservice类名或接口名
soap transport 访问方式,soap
operation name webservice方法名
input message 输入参数
output message 返回参数
回顾练习
看到这里,可以写一个简单的项目,加深一下对WebService的理解。
项目描述:创建一个Servlet项目,页面中提供几个省市的信息,根据选择的省市,返回对应的城市天气(访问第三方接口)
1、通过天气服务的WSDL文件,生成代码。这里直接使用发布的WSDL文件会有错误,所以将这个文件另存为本地,去掉错误的约束,将wsimport中的路径指向本地。
2、引入servlet-api.jar、jsp-api.jar以及刚刚生成的jar包。
3、Servlet代码
import cn.ws.wather.service.WeatherService; import cn.ws.wather.service.impl.WeatherServiceImpl; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; public class WeatherServlet extends HttpServlet { private WeatherService ws; @Override public void init() throws ServletException { ws = new WeatherServiceImpl(); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("UTF-8"); String code = req.getParameter("code"); String result = ws.getWatherByCode(code); resp.setContentType("text/html; charset=UTF-8"); PrintWriter pw = resp.getWriter(); pw.write(result); pw.flush(); pw.close(); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doGet(req, resp); } }
4、业务类代码
public interface WeatherService { public String getWatherByCode(String code); }
import cn.com.webxml.WeatherWS; import cn.com.webxml.WeatherWSSoap; import cn.ws.wather.service.WeatherService; import java.util.List; public class WeatherServiceImpl implements WeatherService { @Override public String getWatherByCode(String code) { WeatherWS ws = new WeatherWS(); WeatherWSSoap soap = ws.getWeatherWSSoap(); List<String> list = soap.getWeather(code, "").getString(); return list.get(4); } }
5、jsp代码,引入jquery.js文件
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>天气预报</title> <script src="js/jquery-1.8.2.js"></script> </head> <body> <select id="city"> <option value="937">济南</option> <option value="963">青岛</option> <option value="911">威海</option> <option value="909">烟台</option> </select> <span>xxx</span> <hr/> <script> $("#city").change(function(){ var code = $(this).val(); $.post( "/weather/weatherService", {"code": code}, function (backData) { $("span").text(backData).css("color", "red"); } ); }); </script> </body> </html>
采用CXF框架发布服务
1、CXF框架概念介绍
Apache CXF 是一个开源的 WebService 框架,CXF可以用来构建和开发 WebService,这些服务可以支持多种协议,比如:SOAP、POST/HTTP、HTTP ,CXF 大大简化了WebService并且可以天然地和 Spring 进行无缝集成。CXF是 Celtrix (ESB框架)和 XFire(webserivice) 合并而成,核心是org.apache.cxf.Bus(总线),类似于Spring的 ApplicationContext,CXF默认是依赖于Spring的,另 CXF 发行包中的jar,如果全部放到lib中,需要 JDK1.6 及以上,否则会报JAX-WS版本不一致的问题。CXF 内置了Jetty服务器 ,它是servlet容器。
2、CXF框架特点
A、与Spring、Servlet做了无缝对接,cxf框架里面集成了Servlet容器Jetty
B、支持注解的方式来发布webservice
C、能够显示一个webservice的服务列表
D、能够添加拦截器:输入拦截器、输出拦截器 :输入日志信息拦截器、输出日志拦截器、用户权限认证的拦截器
3、CXF如何发布服务?
1、创建一个maven项目,添加以下依赖。
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <cxf.version>2.7.1</cxf.version> </properties> <dependencies> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-frontend-jaxws</artifactId> <version>${cxf.version}</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-transports-http</artifactId> <version>${cxf.version}</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-transports-http-jetty</artifactId> <version>${cxf.version}</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-ws-security</artifactId> <version>${cxf.version}</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-ws-policy</artifactId> <version>${cxf.version}</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-bundle-jaxrs</artifactId> <version>${cxf.version}</version> </dependency> <dependency> <groupId>javax.ws.rs</groupId> <artifactId>jsr311-api</artifactId> <version>1.1.1</version> </dependency> </dependencies>
2、创建业务类
/** * 业务接口 */ @WebService(serviceName = "languageManager") public interface LanguageService { public @WebResult(name = "language") String getLanguageInfo(@WebParam(name = "rank") int rank); } /** * 业务实现 */ public class LanguageServiceImpl implements LanguageService { @Override public String getLanguageInfo(int rank) { String language = ""; // 根据rank的值获取语言排名 switch (rank){ case 1: language = "Python"; break; case 2: language = "Java"; break; case 3: language = "PHP"; break; case 4: language = "C#"; break; default: language = "不清楚"; break; } return language; } /** * 测试类 */ public static void main(String[] args) { LanguageService bean = new LanguageServiceImpl(); JaxWsServerFactoryBean factory = new JaxWsServerFactoryBean(); factory.setAddress("http://127.0.0.1:8701/ws/cxf/language"); factory.setServiceClass(LanguageService.class); factory.setServiceBean(bean); // 添加日志输入输出拦截器 factory.getInInterceptors().add(new LoggingInInterceptor()); factory.getOutInterceptors().add(new LoggingOutInterceptor()); // 创建发布WebService factory.create(); } }
发布成功后,可以按照之前的方式访问WSDL,然后编写客户端测试,这里不再赘述。
Spring整合CXF发布服务
在上个项目的基础上,在pom中添加如下依赖。
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <org.springframework.version>3.1.1.RELEASE</org.springframework.version> <cxf.version>2.7.1</cxf.version> <junit.version>4.8.1</junit.version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-oxm</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc-portlet</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${org.springframework.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.6</version> </dependency> <dependency> <groupId>commons-httpclient</groupId> <artifactId>commons-httpclient</artifactId> <version>3.0</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.3</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> </dependencies>
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <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>/ws/*</url-pattern> </servlet-mapping> </web-app>
实体类
public class Employee { private Integer id; private String name; private Integer age; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "Employee{" + "id=" + id + ", name=‘" + name + ‘\‘‘ + ", age=" + age + ‘}‘; } }
业务类
@WebService public interface EmployeeService { public void add(Employee employee); public List<Employee> query(); } public class EmployeeServiceImpl implements EmployeeService { private List<Employee> emps = new ArrayList<>(); @Override public void add(Employee employee) { emps.add(employee); } @Override public List<Employee> query() { return emps; } }
applicationContext.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" 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"> <bean id="empImpl" class="cn.ws.cxf.service.impl.EmployeeServiceImpl"></bean> <jaxws:server address="/employeeManager" serviceClass="cn.ws.cxf.service.EmployeeService"> <jaxws:serviceBean> <ref bean="empImpl"/> </jaxws:serviceBean> <jaxws:inInterceptors> <bean class="org.apache.cxf.interceptor.LoggingInInterceptor"></bean> </jaxws:inInterceptors> <jaxws:outInterceptors> <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor"></bean> </jaxws:outInterceptors> </jaxws:server> </beans>
启动Tomcat,访问http://localhost:8080/springcxfcore/ws/employeeManager?WSDL,出现WSDL文件,则发布成功。