在JAVA EE应用程序中实现Web Service服务(二)代码实战

使用jdk方式发布web service服务

利用jdk工具包中为我们开发者提供的工具,来创建Web Service服务端,同时,使用jdk工具包中的工具来实现访问Web Service的客户端。

服务端

步骤:

  1. 创建一个服务接口 - interface
  2. 创建服务实现类
  3. 发布服务
客户端

步骤:

  1. 生成客户端代码
    wsdl2java -d e:/jdk-client http://localhost:8080/hello?wsdl
  2. 调用远程服务

- 服务器端的代码实现

  1. 创建服务接口
package com.golden3young.service;

import javax.jws.WebService;

// 在服务接口和实现类上都要加上@WebService注解    
// serviceName对应到 WSDL文件中的
// portName对应到WSDL文件中的

@WebService(serviceName="HelloServiceWS", portName="HelloServiceWSSoap")
public interface HelloService {
	//服务方法
	String sayHello(String name);
}

  1. 创建服务实现类
package com.golden3young.service;

import javax.jws.WebService;

@WebService(serviceName="HelloServiceWS", portName="HelloServiceWSSoap")
public class HelloServiceImpl implements HelloService {

	@Override
	public String sayHello(String name) {
		System.out.println("sayHello invoke!");
		return "Hello " + name;
	}
	
}

  1. 发布服务
package com.golden3young;

import javax.xml.ws.Endpoint;

import com.golden3young.service.HelloServiceImpl;

public class JdkWSServer {

	public static void main(String[] args) {
		Endpoint.publish("http://localhost:8080/hello?wsdl", new HelloServiceImpl());
	}

}

运行此方法即可发布服务,我们只需在浏览器中输入规定的路径,即可访问到响应的xml文件。xml文件中的具体标签含义,已在本系列(一)的文章中做过介绍。

- 客户器端的代码实现

  1. 生成客户端代码

我们借助的是apache-cxf的第三方工具,这里面为我们提供了下载服务器端Web Service的工具,我们只需要在安装后,bin路径下,打开cmd命令行面板。输入“ wsdl2java -d e:/jdk-client http://localhost:8080/hello?wsdl ”命令,即可自动为我们将指定路径中的项目代码进行下载。

wsdl2java -d e:/jdk-client http://localhost:8080/hello?wsdl
解释一下:

wsdl2java 是一个命令

-d 表示解压到指定目录下

e:/jdk-client 是我们指定的文件输出路径,第三方工具回将服务器端的代码下载到我们指定的目录下,如果文件夹不存在,也会自动创建。

http://localhost:8080/hello?wsdl 这是我们访问的Web Service服务器端的路径。只有通过正确的路径,才能访问到我们想要的资源代码。

注意,这个下载过程需要联网,没有网络支持是无法完成的。下载完毕后,将目录下的代码文件全部拷贝到我们的工程中,以便我们使用。
其实,下载的所有代码,其实就是服务器端所部署的代码,但是,所有获取数据的代码都被底层加工成了发送请求获取的方式,也就是说,我们下载在客户端的代码,基本与服务器端的代码相同,但是所有获取返回数据的代码,都会被修改成发送请求到指定路径的方式来获取。也就是说,我们客户端的代码,在运行起来以后,其实底层就是继续发送请求到事先指定的路径拿取数据,然后将数据进行处理,处理所需要的类方法或者类属性都已经下载在客户端了。当我们使用的时候,无需关注底层的代码实现,我们只需要通过调用服务对象即可拿到我们想要的值,中间的所有处理,都是刚才下载到本地的代码去执行的。

  1. 调用远程服务
import com.golden3young.service.HelloServiceImpl;
import com.golden3young.service.HelloServiceWS;

public class JdkWSClient {

	public static void main(String[] args) {
		// 
		
		HelloServiceWS ws = new HelloServiceWS();
		//通过service服务名字获取port
		// 这就相当于是xml文件中的那个标签
		// getHelloServiceWSSoap(); => 会返回一个远程代理对象
		HelloServiceImpl helloServiceWSSoap = ws.getHelloServiceWSSoap();
		
		//简单打印一下,看看这个对象是个啥
		System.out.println(helloServiceWSSoap);
		
		//调用这个对象的sayHello()方法,获取结果并打印
		String result = helloServiceWSSoap.sayHello("Web Service");
		System.out.println(result);
	}

}

使用CXF方式发布web service服务

官方地址:cxf.apache.org

	简单介绍:
	
	   Apache CXF = Celtix + XFire,开始叫 Apache CeltiXfire,后来更名为 Apache CXF 了,
	以下简称为 CXF。CXF 继承了 Celtix 和 XFire 两大开源项目的精华,提供了对 JAX-WS 
	全面的支持,并且提供了多种 Binding 、DataBinding、Transport 以及各种 Format 的支
	持,并且可以根据实际项目的需要,采用代码优先(Code First)或者 WSDL 优先(WSDL 
	First)来轻松地实现 Web Services 的发布和使用。Apache CXF已经是一个正式的
	Apache顶级项目。

	    支持 Web Services 标准:CXF 支持多种 Web Services 标准,包含 SOAP、Basic 
    Profile、WS-Addressing、WS-Policy、WS-ReliableMessaging 和 WS-Security。
    Frontends:CXF 支持多种“Frontend”编程模型,CXF 实现了JAX-WS API (遵循 JAX-WS
 	2.0 TCK 版本),它也包含一个“simple frontend”允许客户端和 EndPoint 的创建,而不需
	要 Annotation 注解。CXF 既支持 WSDL优先开发,也支持从 Java 的代码优先开发模式。
	容易使用: CXF 设计得更加直观与容易使用。有大量简单的 API 用来快速地构建代码优先
	的 Services,各种 Maven 的插件也使集成更加容易,支持 JAX-WS API ,支持 Spring 2.0 
	更加简化的 XML 配置方式,等等。支持二进制和遗留协议:CXF 的设计是一种可插拨的
	架构,既可以支持 XML ,也可以支持非 XML 的类型绑定,比如:JSON 和 CORBA。
引入maven依赖
<dependency>  
	<groupId>org.apache.cxf</groupId>  
	<artifactId>cxf-rt-frontend-jaxws</artifactId>
	<version>3.1.11</version> 
</dependency> 

<dependency>  
	<groupId>org.apache.cxf</groupId>  
	<artifactId>cxf-rt-transports-http</artifactId> 
	<version>3.1.11</version> 
</dependency> 

<!--  Jetty is needed if you're are not using the CXFServlet --> 
<dependency>  
	<groupId>org.apache.cxf</groupId>  
	<artifactId>cxf-rt-transports-http-jetty</artifactId>
	<version>3.1.11</version> 
</dependency>
服务端

步骤:

  1. 开发服务接口
  2. 开发服务实现
  3. 暴露服务
    3-1 创建服务工厂
    3-2 设置工厂地址
    3-3 设置要发布的服务接口
    3-4 设置服务实现
    3-5 创建服务
    3-6 启动服务
客户端

步骤:

  1. 生成客户端代码
    wsdl2java -d e:/jdk-client http://localhost:9090/user?wsdl
  2. 调用远程服务
    2-1 创建代理工场
    2-2 设置服务地址
    2-3 设置服务接口
    2-4 创建远程服务代理对象
    2-5 调用远程服务

- 服务器端的代码实现

1.开发服务接口

package com.golden3young.server;

import javax.jws.WebService;

import com.etoak.bean.User;

@WebService	//要记得添加这个注解!以此来表示自己是WebService
public interface UserService {

	User queryUserById(int id);
	
}

2.开发服务实现

package com.golden3young.server.impl;

import javax.jws.WebService;

import com.golden3young.bean.User;
import com.golden3young.server.UserService;

@WebService	//必须添加这个注解!以此来表示自己是WebService
public class UserServiceImpl implements UserService {

	@Override
	public User queryUserById(int id) {
		System.out.println("queryById invoke");
		return new User(id,"Tom",19);
	}

}

  1. 暴露服务
    3-1 创建服务工厂
    3-2 设置工厂地址
    3-3 设置要发布的服务接口
    3-4 设置服务实现
    3-5 创建服务
    3-6 启动服务
package com.golden3young;

import org.apache.cxf.endpoint.Server;
import org.apache.cxf.jaxws.JaxWsServerFactoryBean;

import com.golden3young.server.UserService;
import com.golden3young.server.impl.UserServiceImpl;

public class CxfServer {

	public static void main(String[] args) {
		//创建服务工厂
		JaxWsServerFactoryBean factory = new JaxWsServerFactoryBean();
		
		//设置Wsdl服务地址 -> 也就是客户只要访问这个url,就可以拿到我们这个项目对外公开的代码
		factory.setAddress("http://localhost:9090/user?wsdl");

		//暴露服务接口
		factory.setServiceClass(UserService.class);

		//设置服务实现类
		factory.setServiceBean(new UserServiceImpl());

		//创建服务,并启动
		Server server = factory.create();
		server.start();
		
		System.out.println("~~~~服务已启动~~~~");
	}

}

- 客户器端的代码实现

  1. 生成客户端代码
wsdl2java -d e:/jdk-client http://localhost:9090/user?wsdl

与jdk中提到的方式一样,都是需要下载服务器端代码到本地。
然后将所有代码拷贝到自己的工程中。

  1. 调用远程服务
    2-1 创建代理工厂
    2-2 设置服务地址
    2-3 设置服务接口
    2-4 创建远程服务代理对象
    2-5 调用远程服务
package com.golden3young.cxf;

import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;

import com.golden3young.server.User;
import com.golden3young.server.UserService;

public class CxfClient {
	public static void main(String[] args) {
		//创建代理工厂
		JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
		//设置服务地址
		factory.setAddress("http://localhost:9090/user?wsdl");
		//设置服务接口
		factory.setServiceClass(UserService.class);
		//创建代理
		UserService service = (UserService) factory.create();
		System.out.println(service);
		//调用服务
		User user = service.queryUserById(10);
		System.out.println(user.getId());
		System.out.println(user.getName());
		System.out.println(user.getAge());
		System.out.println(user);
	}
}

这里有个地方需要注意,我们在服务器端生成的代码,一旦被用户下载到本地,进行使用的时候,客户端的程序是依赖于客户端的代码执行的。举一个很直接的例子:如果我们在服务器端自定义的User类中,并没有重写toString()方法,那么一但打印User对象,结果其实是 类名@地址映射值,而如果此时,我们在客户端下载下来的代码中,给uUser类重写toString()方法,那么在客户端中其他代码中再打印User对象的时候,就不会像服务器端那样打印类名@地址映射值 了,而是会依赖于我们拷贝到工程中的代码来执行,由于我们重写了toString,那么打印的结果就会变成我们指定的内容。
同时,一旦服务器端对代码进行了修改,是不会自动同步到客户端代码上的,而是需要客户自己重新访问url,重新下载新的代码,并拷贝到自己的工程中,才能使用更新后的代码。

复习一下:如果用 jdk的方式来获取上面的这个Web Service的服务,也是可以的。
package com.golden3young.jdk;

import com.golden3young.server.User;
import com.golden3young.server.UserService;
import com.golden3young.server.UserServiceService;

public class JdkClient {

	public static void main(String[] args) {
		//这里使用是xml中的serviceName,但此时由于服务器端是利用cxf部署的,
	    //所以不再像刚才jdk工具生成发xml那种格式了。
	    //通过UserServiceSerivce就不难看出它们两个的差别
		//获取Service对象
		UserServiceService us = new UserServiceService();
		
		//通过Service服务名字获取port
		UserService service = us.getUserServicePort();
		
		//调用这个对象的方法并获得返回值
		User user = service.queryUserById(10);
		
		//打印验证
		System.out.println(user.getId());
		System.out.println(user.getName());
		System.out.println(user.getAge());
		System.out.println(user);
	}

}

CXF整合Spring,开发Web Service

- 整合spring web,开发服务端

步骤:

  1. 创建spring-web.xml
  2. 创建spring-context.xml
  3. 配置CXFServlet
  4. 开发服务接口
  5. 开发服务实现
  6. 配置spring容器
  7. 部署到tomcat、测试
引入maven依赖
<dependencies>
	<dependency>
		<groupId>org.apache.cxf</groupId>
		<artifactId>cxf-rt-frontend-jaxws</artifactId>
	</dependency>
	
	<dependency>
		<groupId>org.apache.cxf</groupId>
		<artifactId>cxf-rt-transports-http</artifactId>
	</dependency>
	
	<!-- spring web -->
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-web</artifactId>
	</dependency>
	
	<!-- spring-context -->
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-context</artifactId>
	</dependency>
	
</dependencies>
修改web.xml
	<servlet>
		<servlet-name>cxf</servlet-name>
		<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
		<init-param>
			<param-name>config-location</param-name>
			<param-value>classpath:spring-cxf.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>cxf</servlet-name>
		<url-pattern>/ws/*
	
开发服务接口
package com.golden3young.service;

import java.util.List;

import javax.jws.WebService;

import com.etoak.bean.User;

@WebService		//必须添加这个标签,用于表明身份
public interface UserService {
	
	int addUser(User user);
	
	List<User> queryAllUsers();
}

开发服务实现
package com.golden3young.service.impl;

import java.util.ArrayList;
import java.util.List;

import javax.jws.WebService;

import org.springframework.stereotype.Service;

import com.golden3young.bean.User;
import com.golden3young.service.UserService;

@Service		//为了告诉spring容器,扫描成一个bean对象。 单例
@WebService		//为了告诉cxf组件,这是一个jaxws服务,表明身份
public class UserServiceImpl implements UserService {

	//不建议在真实开发中如此书写(创建全局集合),仅为测试使用
	List<User> users = new ArrayList<>();
	
	@Override
	public int addUser(User user) {
		users.add(user);
		return users.size();
	}

	@Override
	public List<User> queryAllUsers() {
		System.out.println("查询所有用户");
		return users;
	}

}

创建spring-cxf.xml,并移动到指定位置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" 
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:jaxws="http://cxf.apache.org/jaxws" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation=
	"http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
	http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context-4.2.xsd
	http://cxf.apache.org/jaxws
	http://cxf.apache.org/schemas/jaxws.xsd">
	
	<context:component-scan base-package="com.etoak" />
	
	 <!-- http://localhost:8080/contextPath/ws/user?wsdl -->
	 <jaxws:server address="/user" serviceClass="com.etoak.service.UserService">
	 	<jaxws:serviceBean>
	 		<ref bean="userServiceImpl" />
	 	</jaxws:serviceBean>
	 </jaxws:server>
</beans>

上面配置的< jaxws:server >标签其实就是替代了之前我们手动创建工厂的过程,接下来对于工厂的配置,由一开始的setter方法,变成了现在的标签或者属性:

	JaxWSServiceFactroyBean [标签<jaxws:server>]
	factory.setAddress  [属性address="/user" ]
	factory.setServiceClass [属性serviceClass="com.etoak.service.UserService"]
	factory.setServiceBean [标签<jaxws:serviceBean> + <ref bean="userServiceImpl" />]
	factory.create()	//spring自动为我们创建
- 整合spring web,开发客户端
引入maven依赖
<dependencies>
   	<dependency>
	  <groupId>org.apache.cxf</groupId>
	  <artifactId>cxf-rt-frontend-jaxws</artifactId>
	</dependency>
	
	<dependency>
	  <groupId>org.apache.cxf</groupId>
	  <artifactId>cxf-rt-transports-http</artifactId>
	</dependency>
	
	<!-- spring-context -->
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-context</artifactId>
	</dependency>
	
	<!-- 
	 Jetty is needed if you're are not using the CXFServlet
	-->
	<dependency>
	  <groupId>org.apache.cxf</groupId>
	  <artifactId>cxf-rt-transports-http-jetty</artifactId>
	  <version>3.1.11</version>
	</dependency>
	
  </dependencies>
使用指令,下载web服务端代码到本地
wsdl2java -d e:/jdk-client http://localhost:9090/user?wsdl

将下载下来的文件,拷贝到自己的工程中,以备使用。

创建测试类,调用使用
package com.golden3young;

import java.util.List;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.golden3young.service.User;
import com.golden3young.service.UserService;

public class SpringClient {
	public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext("spring-cxf.xml");
		UserService service = (UserService)context.getBean("userService");
		User user = new User();
		user.setId(1);
		user.setName("Tom");
		user.setAge(22);
		int result = service.addUser(user);
		//System.out.println(result);
		List<User> userList = service.queryAllUsers();
		userList.forEach(System.out::println);
	}
}
配置spring-cxf.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" 
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:jaxws="http://cxf.apache.org/jaxws" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
	http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context-4.2.xsd
	http://cxf.apache.org/jaxws
	http://cxf.apache.org/schemas/jaxws.xsd">

	 <!-- jaxws:client 相当于一个远程代理  是一个spring的Bean -->
	 <jaxws:client id="userService" address="http://localhost:8080/cxf-spring-web/ws/user?wsdl"
	 	serviceClass="com.etoak.service.UserService">
	 </jaxws:client>
</beans>

以上< jaxws:client >标签,其实相当于我们自己利用cxf组件创建WebService客户端时书写的代码,只不过现在由spring自动为我们构建。

		JaxWSProxyFactoryBean	[<jaxws:client>]
		factory.setAddress()	[address属性]
		factory.setServiceClass()	[serviceClass属性]
		factory.create() 	//返回一个远程服务的代理对象

你可能感兴趣的:(Web,Service,spring,java,后端)