利用jdk工具包中为我们开发者提供的工具,来创建Web Service服务端,同时,使用jdk工具包中的工具来实现访问Web Service的客户端。
步骤:
步骤:
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);
}
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;
}
}
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文件中的具体标签含义,已在本系列(一)的文章中做过介绍。
我们借助的是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服务器端的路径。只有通过正确的路径,才能访问到我们想要的资源代码。
注意,这个下载过程需要联网,没有网络支持是无法完成的。下载完毕后,将目录下的代码文件全部拷贝到我们的工程中,以便我们使用。
其实,下载的所有代码,其实就是服务器端所部署的代码,但是,所有获取数据的代码都被底层加工成了发送请求获取的方式,也就是说,我们下载在客户端的代码,基本与服务器端的代码相同,但是所有获取返回数据的代码,都会被修改成发送请求到指定路径的方式来获取。也就是说,我们客户端的代码,在运行起来以后,其实底层就是继续发送请求到事先指定的路径拿取数据,然后将数据进行处理,处理所需要的类方法或者类属性都已经下载在客户端了。当我们使用的时候,无需关注底层的代码实现,我们只需要通过调用服务对象即可拿到我们想要的值,中间的所有处理,都是刚才下载到本地的代码去执行的。
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.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。
<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.开发服务接口
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);
}
}
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("~~~~服务已启动~~~~");
}
}
wsdl2java -d e:/jdk-client http://localhost:9090/user?wsdl
与jdk中提到的方式一样,都是需要下载服务器端代码到本地。
然后将所有代码拷贝到自己的工程中。
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,重新下载新的代码,并拷贝到自己的工程中,才能使用更新后的代码。
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);
}
}
步骤:
<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>
<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;
}
}
<?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自动为我们创建
<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>
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);
}
}
<?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() //返回一个远程服务的代理对象