目录
*推荐访问方式 wsimport 生成本地代理(本地有文件)(jdk6之后)
β.需求
需求:
- 不同的移动客户端访问(客户端无法调用服务端内部的方法)
- 需要访问第三方的项目 (比如查个天气、查手机的归属地等)
-1.使用Socket(传输层)
*Socket原理
Server代码:
package com.tencent.server;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class SocketSer {
public static void main(String[] args) throws Exception {
// 1.建立服务器端的TCP Socket服务并监听指定端口
ServerSocket ss = new ServerSocket(7777);
// 2.创建从客户端通过ServerSocket监听的端口传来的Socket对象
Socket s = null;
// 2.1 使用flag是为了写ss.close()不提示不可达语句的错误
boolean flag = true;
// 3.获取客户端的数据
while(flag) {
s = ss.accept();
System.out.println("Accept Success!!!");
//从Socket输入流获取客户端发送过来的输出流
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
String str = new String(buf, 0, len);
System.out.println("客户端传来的数据:"+str);
OutputStream out = s.getOutputStream();
out.write(str.toUpperCase().getBytes());
out.close();
in.close();
s.close();
}
ss.close();
}
}
Client代码:
package com.tencent.client;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
public class SocketClient {
public static void main(String[] args) throws Exception{
Scanner input = new Scanner(System.in);
// 1.创建一个从指定端口传递到服务器端的Socket对象
Socket s = new Socket("127.0.0.1",7777);
// 2.用Socket对象来获取Socket中的输出流
OutputStream out = s.getOutputStream();
System.out.print("请输入要转换到大写的字母、单词:");
String word = input.next();
out.write(word.getBytes());
// 3.通过建立的Socket对象的输入流,获取来自服务器端的数据
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
// 4.获取输入字节流的数据,注意此方法是堵塞的,如果没有获取数据会一直等待
int len = in.read(buf);
String str = new String(buf, 0, len);
System.out.println("服务器返回的结果是"+ str);
in.close();
out.close();
s.close();
}
}
*Socket的通信特点
1)开启端口,该通信是 长连接的通信 ,很容易被防火墙拦截,可以通过心跳机制(过几秒传个几字节)来实现 ,开发难度大
2)传输的数据一般是字符串 ,可读性不强
lj|16|1|60|up
3)socket端口不便于推广
http:17.23.23.2:2345 www.jd.com www.360buy.com
4)性能相对于其他的通信协议是最优的
*再来复习一下OSI的七层模型:
Socket属于传输层,它是对Tcp/ip协议的实现,包含TCP/UDP,它是所有通信协议的基础
,Http协议需要Socket支持,以Socket作为基础
*如果对于一些复杂一点的需求,比如又要小写转大写、又要大写转小写的需求,可以采用"Socket+规定格式"来创建Web服务
但是这样有缺点:不利于推广(别人不知道你的服务器端需要客户端传怎样的格式)
0.使用Http协议访问(应用层)
属于应用层的协议,对Socket进行了封装(也就是以Socket为基础,加多了点东西,比Socket更加笨重),短连接通信
*特点
1)跨平台(大部分操作系统都支持HTTP)
2)传数据的方式不够友好:get请求: http://127.0.0.1:8888?username=lj&pwd=1234
最好可以直接传一个对象过来,因为属性一多就会变成一长串
3)对第三方应用提供的服务,希望对外暴露服务接口(因为返回的是一些页面)
1.WebService技术和规则
*前面两种访问第三方项目的方法都存在问题:
- 数据封装不够友好 (XML)
-希望给第三方应用提供web方式的服务 (HTTP + XML = WebService)
*通俗的讲,Web Service就是一个部署在Web服务器上的一个,它向外界暴露出一个能够通过Web进行调用的API
*WebService技术和规则
--->xml(可拓展标记语言)数据通过XML来进行封装
--->soap(简易对象访问协议simple object access protocal) soap 依赖-->http+xml-->socket
使用soap来以web的形式来调用外部的service服务(暴露服务),如果在内部的service就只能用java了
- Envelope – 必须的部分。以XML的根元素出现。
- Headers – 可选的。
- Body – 必须的。在body部分,包含要执行的服务器的方法。和发送到服务器的数据.
eg:getDatabaseInfoResponse方法(获得国内手机号码归属地数据库信息)
HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Content-Length: length
<?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>
<getDatabaseInfoResponse xmlns="http://WebXml.com.cn/">
<getDatabaseInfoResult>
<string>string</string>
<string>string</string>
</getDatabaseInfoResult>
</getDatabaseInfoResponse>
</soap:Body>
</soap:Envelope>
--->wsdl(web services 描述语言) 教你怎么调用service,怎么使用里面的方法
- 通过XML形式说明服务在什么地方-地址。
- 通过XML形式说明服务提供什么样的方法 – 如何调用。
eg:http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx?WSDL
--->uddi(通用描述、发现及整合) -->>推广不好
有一个全球的WebService目录,收集了大部分的WebService(注册、登记之后)
就可以通过uddi来找到想要的WebService(唯一标识)
2.访问WebService的方式
- 使用别人已准备好的服务器端www.webxml.com.cn
- 自己准备客户端,调用第三方的WebService服务
这些访问方式只是抽取出业务层的接口,并没有真正获取到业务层的对象
所以在使用时是这样的
先获取到的是服务
再通过服务获取到到这个业务类的接口(phoneServicePort)
因为WebService本来就是要调用服务器的方法,而不是调用本地有的方法,所以不可能是获得真正的对象
* 一般访问方式 HttpClient
1. Http-GET
2. Http-Post
3. Soap (http+xml)
写法参考http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx?op=getMobileCodeInfo
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.PostMethod;
/**调用第三方的webservice服务 ,获取电话号码信息
*
*/
public class MobileCodeService {
//1. http-get方式访问webservice
public void get(String mobileCode ,String userID ) throws Exception{
URL url=new URL("http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx/getMobileCodeInfo?mobileCode="+mobileCode+
"&userID="+userID);
HttpURLConnection conn=(HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
if(conn.getResponseCode()==HttpURLConnection.HTTP_OK){ //结果码=200
InputStream is=conn.getInputStream();
//内存流 ,
ByteArrayOutputStream boas=new ByteArrayOutputStream();
byte[] buffer=new byte[1024];
int len=-1;
while((len=is.read(buffer))!=-1){
boas.write(buffer, 0, len);
}
System.out.println("GET请求获取的数据:"+boas.toString());
boas.close();
is.close();
}
}
//2.Post请求 :通过Http-Client 框架来模拟实现 Http请求
public void post(String mobileCode ,String userID) throws Exception{
/**HttpClient访问网络的实现步骤:
* 1. 准备一个请求客户端:浏览器
* 2. 准备请求方式: GET 、POST
* 3. 设置要传递的参数
* 4.执行请求
* 5. 获取结果
*/
HttpClient client=new HttpClient();
PostMethod postMethod=new PostMethod("http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx/getMobileCodeInfo");
//3.设置请求参数
postMethod.setParameter("mobileCode", mobileCode);
postMethod.setParameter("userID", userID);
//4.执行请求 ,结果码
int code=client.executeMethod(postMethod);
//5. 获取结果
String result=postMethod.getResponseBodyAsString();
System.out.println("Post请求的结果:"+result);
}
SOAP1.1方式:
public static void postSoap1() throws Exception {
// 创建一个post请求,类似Post请求
PostMethod postMethod = new PostMethod("http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx");
// 设置传送信息的格式
postMethod.setRequestHeader("Content-Type","text/xml; charset=utf-8");
//a.txt是请求体的内容---》依赖XML的地方
postMethod.setRequestBody(new FileInputStream("C:/a.txt"));
int code = http.executeMethod(postMethod);
System.out.println("消息码为:" + code);
System.out.println("返回的消息为:" + postMethod.getResponseBodyAsString());
postMethod.releaseConnection();
}
public static void main(String[] args) throws Exception{
MobileCodeService ws=new MobileCodeService();
ws.get("18333333333", "");
ws.post("18333333333", "");
ws.postSoap1();
}
}
eg:get方法返回的数据
GET /WebServices/MobileCodeWS.asmx/getMobileCodeInfo?mobileCode=string&userID=string HTTP/1.1
Host: ws.webxml.com.cn
HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Content-Length: length
<?xml version="1.0" encoding="utf-8"?>
<string xmlns="http://WebXml.com.cn/">我们所需要的信息(电话号码18333333333归属地)</string>
使用这种方式会有问题:
- 如何解析结果(因为依赖soap返回的是XML,但是我们只需要String)
- 如何传递对象参数 (每个方法要写解析器,因为每个XML的标签不一样)
*推荐访问方式 wsimport 生成本地代理(本地有文件)(jdk6之后)
就是把提供服务的代码(class文件)下载到本地
wsimport命令位置:C:\Program Files (x86)\Java\jdk1.8.0_161\bin
- 要求:
- jdk的 版本要在 jdk 1.6.21及以上
- 操作系统安装的jdk版本 与 Eclispe 及 默认指定的版本要一致
记得设置jdk\bin 环境变量 指定path
语法 wsimport [opations] <wsdl_uri>
- wsdl_uri:wsdl 的统一资源标识符
- -d :指定要输出的文件的位置
- -s :表示要解析java的源码 ,默认解析出的是class字节码
- -p : 指定输出的包名
3.WSDL语法
3.1 eg:
3.2 @WebService 修改WSDL文件
WebService的注解包括:
- @WebService-定义服务 --类上
- @WebMethod-定义方法 - 方法
- @WebResult-定义返回值 – 返回值
- @WebParam-定义参数 – 参数
WebService注解的使用
- 通过WebService的注解,可以更加形像的描述Web服务。从而生成WSDL文档。
- 当修改了WebService注解之后,同时会影响客户端生成的代码。
- 调用的方法名和参数名也发生了变化。
- 即使是没有修改源代码,只修改了注解,客户端的代码也必须要重新生成(注意是生成而不是下载)。否则调用将会失败。
- 生成本地调用代码,依然使用wsimport工具
@WebService注解:
- @WebService 标注要暴露为Web Services的类或接口 ,用于修饰类或接口,包含的属性有:
- targetNamespace属性:定义命名空间,默认为”http://”+”包名倒排”
- name属性:Web Service 的名称,默认为发布服务的类名。
- serviceName: ws服务的名词,默认在类名后面添加了service
- endpointInterface属性:定义服务抽象 Web Service 协定的服务端点接口的完整名称,接口也必须声明WebService注解,包括方法的注解必须也要添加到接口中,否则会无效, 而且WS在没有注解的情况下.生成WS的时候会自动生成一个注解.所以可以不用指定接口
@WebMethod
- 此注解用在方法上,用于修改对外暴露的方法
- operationName属性:与此方法匹配的 wsdl:operation 的名称
- exclude属性:标注此方法是否被暴露,默认为false
注意:如果所有方法上都没有指定@WebMethod,则默认是所有的方法都是对外暴露的方法。如果有任一方法指定了@WebMethod,则只有指定这个注解的才是对外暴露的方法。
@WebResult
@WebResult 定义返回值,返回值类型不能为接口类或抽象类,而且必须有个不带参的构造函数,包含属性
name属性:返回值的名称
4.CXF框架
4.1 CXF介绍
- SOA的框架
- Cxf 是 Celtrix (ESB框架)和 XFire(webserivice) 合并而成,并且捐给了apache
- CxF的核心是org.apache.cxf.Bus(总线),类似于Spring的 ApplicationContext
- CXF默认是依赖于Spring的
- Apache CXF 发行包中的jar,如果全部放到lib中,需要 JDK1.6 及以上,否则会报JAX-WS版本不一致的问题
- CXF 内置了Jetty服务器 ,它是servlet容器,好比tomcat
4.1.1 什么是SOA
- Soa :面向服务的架构,它是一种思想,IBM大力倡导
service 1 、service2 、Service3 , 服务都是面向web的 ,而且是即插即用的
IBM大力提倡,希望以组装电脑的方式来开发应用
- 组成:
1. 面向web的服务,面向web的组件 :WebService : 硬盘、cpu、内存条
2. 企业服务总线 (EnterPrise Service Bus :ESB)。主板
4.2 为什么要使用CXF
通过Jdk
需要声明 :@Webservice
发布使用 :EndPoint (Endpoint.publish(address, new Service()))
不足: 希望tomcat启动时,webservice服务能够开启 ,最好与Spring集成
希望有一个webservice的服务列表
通过Webservice框架来实现 :axis2 、xfire
CXF XFire Axis2 JWS都是些什么:https://blog.csdn.net/TypantK/article/details/83964419
4.3 CXF不整合Spring发布WebService
/**通过cxf框架发布webservice
* 1. ServerFactoryBean
* - 不设置注解也可以发布webservice服务, 不支持注解
* - 不支持拦截器的添加
* 2. JaxWsServerFactoryBean
* - 支持注解
* - 可以添加拦截器
* @param args
*/
public static void main(String[] args) {
LanguageService languageService=new LanguageServiceImpl();
ServerFactoryBean bean=new ServerFactoryBean();
//Endpoint :地址 , 实现对象
bean.setAddress("http://192.168.114.10:9999/ws/cxf/languangeService");
bean.setServiceClass(LanguageService.class);//对外提供webservcie的业务类或者接口
bean.setServiceBean(languageService);//服务的实现bean
bean.create();//创建,发布webservice
System.out.println("wsdl地址:http://192.168.114.10:9999/ws/cxf/languangeService?WSDL");
}
public static void main(String[] args) {
LanguageService languageService=new LanguageServiceImpl();
JaxWsServerFactoryBean bean=new JaxWsServerFactoryBean();
//Endpoint :地址 , 实现对象
bean.setAddress("http://192.168.114.10:9999/ws/cxf/languangeService");
bean.setServiceClass(LanguageService.class);//对外提供webservcie的业务类或者接口
bean.setServiceBean(languageService);//服务的实现bean
//添加输入拦截器 :输入显示日志信息的拦截器
bean.getInInterceptors().add(new LoggingInInterceptor());
//添加输出拦截器 :输出显示日志信息的拦截器
bean.getOutInterceptors().add(new LoggingOutInterceptor());
bean.create();//创建,发布webservice
System.out.println("wsdl地址:http://192.168.114.10:9999/ws/cxf/languangeService?WSDL");
}
4.4 CXF整合Spring发布WebService
主要就是添加一个CXF请求的 Servlet,用来处理webservice的请求 (web.xml)
<servlet>
<servlet-name>cxf</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>cxf</servlet-name>
<url-pattern>/ws/*</url-pattern>
</servlet-mapping>
还有配置CXF的bean在applicationContext.xml中
...
xmlns:jaxws="http://cxf.apache.org/jaxws"
...
<bean id="employeeManagerImpl" class="cn.it.ws.cxf.b.EmployeeManagerImpl"></bean>
<!-- 配置cxf
地址: http://192.168.114.10:8080/CXF_Server/ws/employeeManager
组成 : http://192.168.114.10:8080 +CXF_Server( 项目名)+ws(过滤的路径)+/employeeManager(自定义部分)
服务类 :
服务的实现类:
拦截器
-->
<jaxws:server address="/employeeManager" serviceClass="cn.it.ws.cxf.b.EmployeeManager">
<jaxws:serviceBean>
<ref bean="employeeManagerImpl"/>
</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>
源码:
Employee.java (bean)
package cn.it.ws.cxf.bean;
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;
}
}
EmployeeManagerImpl.java
package cn.it.ws.cxf.b;
import java.util.ArrayList;
import java.util.List;
import cn.it.ws.cxf.bean.Employee;
public class EmployeeManagerImpl implements EmployeeManager {
private List<Employee> employees=new ArrayList<>();
@Override
public void add(Employee employee){
//添加到集合中
employees.add(employee);
}
@Override
public List<Employee> query(){
return employees;
}
}
EmplyeeManager.java
package cn.it.ws.cxf.b;
import java.util.List;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import cn.it.ws.cxf.bean.Employee;
@WebService(serviceName="EmployeeService")
public interface EmployeeManager {
public abstract void add(@WebParam(name="employee")Employee employee);
public abstract @WebResult(name="employees")List<Employee> query();
}
5. webservice 访问流程:
- 检测本地代理描述的wsdl是否与服务端的wsdl一致 ,俗称为握手
- 通过soap协议实现通信 ,采用的是post请求 , 数据封装在满足soap规约的xml中
- 返回数据 同样采用的是soap通信, 数据封装在满足soap规约的xml中