webservice和CXF基础

一、什么是WebService

  1. WebService直译网络服务,是RPC的一种实现方式。
  2. RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议
  3. 客户端要能调用服务端必须遵循TCP协议,而WebService使用的是更高一级的HTTP协议。
  4. 客户端访问服务器的目的是为了获取数据,那数据必须是与平台、语言无关的。WebService采用的是XML.

二、使用场景

  1. 由权威机构提供的第三方小功能。

        比如Webxml等,发布了一些服务。我们作为客户端来使用。

    2.多种客户端的整合。      

      一些大型应用公司(比如腾讯)为了提高影响力,它会针对不同的平台提供开发不同的客户端。而客户端需要调用获取数据,使用RPC,WS就是一种实现方式。而根据运行环境不同,客户端分为:PC端(web,PC客户端),手机端(web,客户端),微信端。

    3.异构系统的整合。

       将多个已经建设好的系统,组合成一个系统,统一对外发布服务。

三、开发基于JDK的webService服务

    1、开发服务端

      开发基于jdk的webService服务,常用的注解都在下面的代码中:

/**
 * 
 * @author lwb 2017年12月28日 下午10:31:31
 *
 *	用户输入姓名,查看年龄
 *
 */
//这里可以改服务的名称,否则为默认的名称,类名+Serive 例子:UserServiceService
@WebService(serviceName="UserService")
//这是默认的格式
//@SOAPBinding(style=Style.DOCUMENT)
@SOAPBinding(style=Style.RPC)
public class UserService {
	private static Map<String,Integer> user = new HashMap<>();
	
	static {
		user.put("小赖", 26);
		user.put("小王", 26);
		user.put("小付", 25);
		user.put("小钟",24);
	}
	//这里有没有WebMethod标签,这个方法都是发向外发布出去的
	//这个方法的名称也是可以改变的,如果不改变就是默认的方法名
	@WebMethod(operationName="getUserAge")
	//@WebParam(name = "getAge")可以在发布的wsdl中看见参数的名称
	public @WebResult(name="userAge") String getAge(@WebParam(name = "getAge")String name) {
		if(name == null) {
			return "错误:请输入姓名";
		}
		if(user.get(name) == null) {
			return "你查找的姓名没有信息";
		}
		return name + ":" + user.get(name);
	}
	
	//如果@WebMethod(exclude=true)
	//那么这个方法就不会被暴露出去
	@WebMethod(exclude=true)
	public int getAge(int age) {
		return age;
	}
}

②发布服务:

import javax.xml.ws.Endpoint;

public class PublicService {
	public static void main(String[] args) {
		//发布服务的地址
		String address = "http://127.0.0.1:8888/user";
		//服务的实现类
		Object implementor = new UserService();
		//发布服务
		Endpoint.publish(address, implementor);
		System.out.println("服务发布成功.....");
	}
}

四、在浏览器中访问发布的服务

    在浏览其中访问发布的服务,访问地址就是上面代码中发布的地址:

    值得注意的是,在JDK7.0之前,可以直接使用地址访问,如:http://127.0.0.1:8888/user

    但是在JDK8.0之后,访问的地址之后需要加上?wsdl,如:http://127.0.0.1:8888/user?wsdl

    访问结果截图如下:(图片看不清楚,自己访问一下就知道了)

五、WSDL文件解释

        在WSDL文件的最外层definitions标签里有两个非常关键的属性,targetNamespace属性和name属性。targetNamespace属性对应的值来自我们的包结构,是以倒置包名的形式呈现。name属性值是在发布服务在WS中的名称:服务名称=服务类名称+Service。

由于图片太大,看不清楚,所以一步一步记录,将xml文件折叠之后:

①service标签:

②binding标签:

③portType标签:

④message标签:

上图中为啥可以看见可以看见参数的名称?就是因为我们代码中修改了名称,否则就是默认的arg0做为参数名称。

所以,看这个xml文件的时候,是从下向上看,看起来就特别简单了。

六@SOAPBinding(style=Style.DOCUMENT)和@SOAPBinding(style=Style.RPC)的区别

    在RPC中,可以看见message标签是这样的,能够看见输入参数和输出参数的类型 和名字:

但是在DOCUMENT中就看不见,但是多了一个<xsd:schema>标签

其中有一个schemalocation属性,访问该地址,就是对输入输出参数的详细介绍了:

为什么会有这样的设计方式,就是为了在参数很多,或者很复杂的情况,可以看到更清楚,更方便,没有别的用意,具体使用哪一种,就视情况而定了。

七、客户端开发

    客户端开发是使用jdk自带的工具自动生成的:

wsimport  -d  .  http://127.0.0.1:8888/user?wsdl

-d:将会在指定目录下生成.class文件

wsimport  -s  .  http://127.0.0.1:8888/user?wsdl

-s:将会在指定目录下生成.java文件

比如,我进入我的项目路径下:

当然,这个时候我们的服务端必须是开启的。然后进入我的客户端,就可以看见生成的代码了:

然后编写一个类,穿件调用客户端对象的方法,就可以了:

拓展:

使用@ SOAPBinding标签,需要把他标记在类上:

SOAPBinding.ParameterStyleparameterStyle 确定方法参数是否表示整个消息正文,或者参数是否是包装在以操作命名的顶层元素中的元素。

SOAPBinding.ParameterStyle.WRAPPED,默认,使用对参数进行包装

SOAPBinding.ParameterStyle.BARE,不对参数进行包装

SOAPBinding.Style style 定义发 送到Web Service 的消息 和从Web Service发送的消息的编码样式。

SOAPBinding.Style.RPC:面向RPC

SOAPBinding.Style.DOCUMENT 默认,面向文档

SOAPBinding.Useuse 定义发送到WebService的消息和从WebService发送的消息的格式样式。

SOAPBinding.Use.LITERAL,默认,字面量风格,若服务端和客户端不在一起开发,就应该使用这个

SOAPBinding.Use.ENCODED使用SOAP编码风格,可能导致WS互操作方面失败问题,尽量避免使用。

八、CXF基础

   

在lib包中选择需要的文件:

    2、服务端开发

服务端的开发,和JDK的开发方式一样,因为CXF是遵循wsdl协议的,所以对JDK的标签也是一样的使用:

首先定义一个接口:

@WebService
public interface StudentScoreInterface {
	@WebMethod
	public int getScore(String studentName);
}

然后实现该接口:

//必须指定对外发布服务的接口
@WebService(endpointInterface="cn.laiwenbo.com.cxf.studentService.StudentScoreInterface")
public class StudentService implements StudentScoreInterface {
	
	private static Map<String,Integer> map = new HashMap<>();
	static {
		map.put("小赖", 100);
		map.put("小王", 90);
		map.put("小李", 80);
		map.put("小张", 70);
	}
	
	@Override
	public int getScore(String studentName) {
		return map.get(studentName);
	}
}

发布服务:

public class PublishStudentService {
	public static void main(String[] args) {
		//jdk的发布方式
		/*String address = "http://127.0.0.1:8888/score";
		Object implementor = new StudentService();
		Endpoint.publish(address, implementor);
		System.out.println("服务发布成功....");*/
		
		//CXF发布方式
		JaxWsServerFactoryBean factoryBean = new JaxWsServerFactoryBean();
		String address = "http://127.0.0.1:8888/score";
		Object implementor = new StudentService();
		//设置服务的地址
		factoryBean.setAddress(address);
		//设置服务的实现类
		factoryBean.setServiceBean(implementor);
		//设置接口
		factoryBean.setServiceClass(StudentScoreInterface.class);
		//发布
		factoryBean.create();
	}
}

    3、客户端开发:

开发客户端,同样是使用工具,由于cxf是没有配置环境变量的,所以需要在CXF的工具文件夹下来执行命令:

wsdl2java -d -p cn.lwb.client http://127.0.0.1:9527/hello?wsdl

-p :可以指定文件生成的位置

. (点):表示当前目录

所以我需要进入到CXF的工具文件夹目录下:

wsdl2java -d G:\laiwenbo\java\eclipse_oxygen\webServicePublish\src(文件生成位置)

cn.itsource.client http://127.0.0.1:9527/hello?wsdl(访问地址)

生成的结果如下:

由于多一个方法,就会多两个文件,比如我的只有一个getScore方法,所以就生成了GetScore.java和

GetScoreResponse.java两个文件。

访问服务:

(1)JDK的访问方式:

    //JDK的访问方式,虽然是用CXF生成的,但是JDK的方式依然是可以访问的
    @Test
	public void jdkClient() throws Exception {
		StudentScoreInterfaceService scoreInterfaceService = new StudentScoreInterfaceService();
		StudentScoreInterface port = scoreInterfaceService.getStudentScoreInterfacePort();
		int score = port.getScore("小李");
		System.out.println(port);
		System.out.println(score);
	}

(2)CXF的访问方式,不需要使用工具生成文件,而是直接调用接口:

/**
	 * CXF的方式调用服务
	 * @throws Exception
	 */
	@Test
	public void testCXF() throws Exception {
		// 1.创建JaxWsProxyFactoryBean的对象,用于接收服务
		JaxWsProxyFactoryBean factoryBean = new JaxWsProxyFactoryBean();
		// 2.设置服务的发布接口,使用本地的代理接口
		factoryBean.setServiceClass(StudentScoreInterface.class);
		// 3.设置服务的发布地址,表示去哪里获取服务
		String address = "http://127.0.0.1:8888/score";
		factoryBean.setAddress(address);
		// 4.通过create方法返回接口代理实例
		StudentScoreInterface service = (StudentScoreInterface)factoryBean.create();
		System.out.println(service.getScore("小赖"));
	}

这么看来,CXF反而比JDK复杂了,好像并没有什么特别的地方,但是注意,我们生成的文件可以全部删掉,除了唯一的一个接口之外:

也就是说,删成这样之后,依然没有问题,这就是CXF的优点,注意,删除之后,在剩下的接口中有一个地方会报错:

这个注解没有什么别的作用,就是说可以看另一个地方的东西而已,注释掉就是了:

(3)CXF的另一种调用方式,动态代理:

JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance();
        org.apache.cxf.endpoint.Client client = dcf.createClient("http://127.0.0.1:9999/weather?wsdl");
        Object[] res = null;
        try {
            res = client.invoke("getWeather", new String[]{"小赖"});
            for (Object obj:res) {
                System.out.println(obj);
            }
        } catch (Exception e) {
            e.printStackTrace();
    }

getWeather:为服务方法名

new String[]{"小赖"} :为传入的参数素组,按照参数顺序排列

(4)axis2调用

以上就是webService的全部基础了,后面如果学习到新的东西,会补充进来。

猜你喜欢

转载自blog.csdn.net/u013441805/article/details/80519126