# webservice-practice
**Repository Path**: monkey12/webservice-practice
## Basic Information
- **Project Name**: webservice-practice
- **Description**: No description available
- **Primary Language**: Java
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2022-04-15
- **Last Updated**: 2022-04-17
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# Apache CXF
cxf 是 java 语言的 webservice 使用框架,使用它开发 webserivce 要比使用原生 API 更加方便,同时它提供了 Spring Boot 的启动器,可以方便和 Spring Boot 集成。
## 两种开发方式
### Jax-ws
pom.xml
```xml
org.apache.cxf
cxf-spring-boot-starter-jaxws
3.5.2
```
创建服务接口
```java
/**
* @WebService 注解标志这是一个 WebService 接口
*/
@WebService
public interface WSServer {
void save(Student student);
void delete(String id);
List findListByStudent(Student student);
}
```
创建 pojo
```java
public class Book{
private String name;
private Date publishDate;
private Integer price;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getPublishDate() {
return publishDate;
}
public void setPublishDate(Date publishDate) {
this.publishDate = publishDate;
}
public Integer getPrice() {
return price;
}
public void setPrice(Integer price) {
this.price = price;
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", publishDate=" + publishDate +
", price=" + price +
'}';
}
}
public class Student{
private String name;
private int age;
private Date birthDay;
private List bookList;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Date getBirthDay() {
return birthDay;
}
public void setBirthDay(Date birthDay) {
this.birthDay = birthDay;
}
public List getBookList() {
return bookList;
}
public void setBookList(List bookList) {
this.bookList = bookList;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", birthDay=" + birthDay +
", bookList=" + bookList +
'}';
}
}
```
创建服务实现类
```java
@WebService
@Slf4j
@Component
public class WSServerImpl implements WSServer {
@Override
public void save(Student student) {
log.info("保存student:{}", student.toString());
}
@Override
public void delete(String id) {
log.info("删除student:{}", id);
}
@Override
public List findListByStudent(Student student) {
log.info("参数student:{}", student);
student = new Student();
student.setName("张三");
student.setAge(120);
student.setBirthDay(new Date());
List studentList = new ArrayList<>();
Book book = new Book();
book.setPublishDate(new Date());
book.setPrice(78);
book.setName("计算机组成原理");
List bookList = new ArrayList<>();
bookList.add(book);
book = new Book();
book.setPublishDate(new Date());
book.setPrice(78);
book.setName("java");
bookList.add(book);
student.setBookList(bookList);
studentList.add(student);
log.info("查询student:{}", studentList);
return studentList;
}
}
```
发布服务
```java
@Configuration
public class WSConfig {
@Autowired
private Bus bus;
@Autowired
private WSServerImpl wsServer;
@Bean
public Endpoint createEndpoint(){
// 注意,服务发布后的地址默认有一个 services 前缀
// http://localhost:8080/services/webservice
EndpointImpl endpoint = new EndpointImpl(bus, wsServer);
endpoint.publish("/webservice");
return endpoint;
}
}
```
生成客户端
下载 cxf
https://cxf.apache.org/download.html
下载最新的版本
[cxf-version.png](./images/cxf-version.png)
解压之后进入bin 目录运行以下命令生成客户端代码
```bash
wsdl2java -d D:\temp http://localhost:8080/services/webservice?wsdl
```
* -d:客户端代码保存目录
将客户端代码复制到项目中,然后使用 CXF API 编写调用 webservice 服务的代码。
调用服务测试
```java
public class ClientTest {
public static void main(String[] args) {
testFind();
}
/**
* 测试保存
*/
public static void testSave(){
JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean();
// 设置服务接口
jaxWsProxyFactoryBean.setServiceClass(WSServer.class);
// 设置服务地址
jaxWsProxyFactoryBean.setAddress("http://localhost:8080/services/webservice?wsdl");
// 创建服务接口代理
WSServer wsServer = jaxWsProxyFactoryBean.create(WSServer.class);
List bookList = new ArrayList<>();
Book book = new Book();
book.setName("必知必会");
book.setPublishDate(date2XMLGregorianCalendar(new Date()));
book.setPrice(120);
bookList.add(book);
book = new Book();
book.setName("设计模式");
book.setPublishDate(date2XMLGregorianCalendar(new Date()));
book.setPrice(78);
bookList.add(book);
Student student = new Student();
student.setAge(12);
student.setName("小红");
student.setBirthDay(date2XMLGregorianCalendar(new Date()));
wsServer.save(student);
}
/**
* 测试查询
*/
public static void testFind(){
JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean();
jaxWsProxyFactoryBean.setServiceClass(WSServer.class);
jaxWsProxyFactoryBean.setAddress("http://localhost:8080/services/webservice?wsdl");
WSServer wsServer = jaxWsProxyFactoryBean.create(WSServer.class);
Book book1 = new Book();
book1.setName("mysql必知必会");
book1.setPublishDate(date2XMLGregorianCalendar(new Date()));
book1.setPrice(120);
Book book2 = new Book();
book2.setName("设计模式");
book2.setPublishDate(date2XMLGregorianCalendar(new Date()));
book2.setPrice(78);
Student student = new Student();
student.setAge(12);
student.setName("小红");
student.setBirthDay(date2XMLGregorianCalendar(new Date()));
student.getBookList().add(book1);
student.getBookList().add(book2);
List listByStudent = wsServer.findListByStudent(student);
listByStudent.forEach(po -> {
System.out.println(po.getName());
System.out.println(po.getAge());
System.out.println(po.getBirthDay());
List bookList1 = po.getBookList();
bookList1.forEach(po1 -> {
System.out.println(po1.getName());
System.out.println(po1.getPrice());
System.out.println(po1.getPublishDate());
});
});
}
/**
* 测试删除
*/
public static void testDelete(){
JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean();
jaxWsProxyFactoryBean.setServiceClass(WSServer.class);
jaxWsProxyFactoryBean.setAddress("http://localhost:8080/services/webservice?wsdl");
WSServer wsServer = jaxWsProxyFactoryBean.create(WSServer.class);
wsServer.delete("123");
}
/**
* Date 转换为 XMLGregorianCalendar
* @param date
* @return
*/
public static XMLGregorianCalendar date2XMLGregorianCalendar(Date date){
XMLGregorianCalendar xmlGregorianCalendar = null;
GregorianCalendar gregorianCalendar = new GregorianCalendar();
gregorianCalendar.setTime(date);
try {
xmlGregorianCalendar = DatatypeFactory.newInstance().newXMLGregorianCalendar(gregorianCalendar);
} catch (DatatypeConfigurationException e) {
e.printStackTrace();
}
return xmlGregorianCalendar;
}
}
```
### Jax-rs
创建服务接口
```java
/**
* restful 风格的接口
*/
public interface WSServer {
@POST // 添加操作
@Consumes(MediaType.APPLICATION_JSON)
void saveStudent(Student student);
@Consumes(MediaType.APPLICATION_JSON)
@PUT // 修改操作
void pudateStudent(Student student);
@DELETE // 删除操作
@Path(value = "/{id}")
void deleteStudent(@PathParam(value = "id") Integer id);
@GET // 查询所有
@Produces(MediaType.APPLICATION_JSON)
List findAllStudent();
@GET // 根据 id 查询
@Path(value = "/{id}")
@Produces(MediaType.APPLICATION_JSON)
Student findById(@PathParam(value = "id") Integer id);
}
```
服务实现类
```java
@Slf4j
@Component
public class WSServerImpl implements WSServer {
@Override
public void saveStudent(Student student) {
log.info("保存student:{}", student);
}
@Override
public void pudateStudent(Student student) {
log.info("更新student:{}", student);
}
@Override
public void deleteStudent(Integer id) {
log.info("删除id:", id);
}
@Override
public List findAllStudent() {
List studentList = new ArrayList<>();
Student student = new Student();
student.setName("小红");
student.setAge(120);
student.setBirthDay(new Date());
Book book = new Book();
book.setName("必知必会");
book.setPrice(20);
book.setPublishDate(new Date());
List bookList = new ArrayList<>();
bookList.add(book);
student.setBookList(bookList);
studentList.add(student);
log.info("查询所有student:{}", studentList);
return studentList;
}
@Override
public Student findById(Integer id) {
Student student = new Student();
student.setName("小红");
student.setAge(120);
student.setBirthDay(new Date());
Book book = new Book();
book.setName("必知必会");
book.setPrice(20);
book.setPublishDate(new Date());
List bookList = new ArrayList<>();
bookList.add(book);
student.setBookList(bookList);
return student;
}
}
```
pojo 类
```java
@XmlRootElement
public class Book{
private String name;
private Date publishDate;
private Integer price;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getPublishDate() {
return publishDate;
}
public void setPublishDate(Date publishDate) {
this.publishDate = publishDate;
}
public Integer getPrice() {
return price;
}
public void setPrice(Integer price) {
this.price = price;
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", publishDate=" + publishDate +
", price=" + price +
'}';
}
}
@XmlRootElement
public class Student{
private String name;
private int age;
private Date birthDay;
private List bookList;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Date getBirthDay() {
return birthDay;
}
public void setBirthDay(Date birthDay) {
this.birthDay = birthDay;
}
public List getBookList() {
return bookList;
}
public void setBookList(List bookList) {
this.bookList = bookList;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", birthDay=" + birthDay +
", bookList=" + bookList +
'}';
}
}
```
发布服务
```java
@Configuration
public class WSConfig {
@Autowired
private Bus bus;
@Autowired
private WSServerImpl wsServer;
@Bean
public Server createEndpoint(){
// 注意,服务发布后的地址默认有一个 services 前缀
// http://localhost:8080/services/webservice
JAXRSServerFactoryBean endpoint = new JAXRSServerFactoryBean();
endpoint.setBus(bus);
endpoint.setAddress("/webservice");
endpoint.setServiceBean(wsServer);
return endpoint.create();
}
}
```
测试调用服务
开发 restful 风格的 webservice 客户端需要用到 cxf 的 WebClient 的工具类,可以发送不同类型的请求。
```java
public class DemoTest {
private final String ADDRESS = "http://localhost:8080/services/webservice";
/**
* 测试保存
*/
@Test
public void saveTest(){
// 发送 POST 请求
WebClient.create(ADDRESS)
// 发送 json 格式数据
.type(MediaType.APPLICATION_JSON)
.post(new Student(), Student.class);
}
/**
* 更新测试
*/
@Test
public void updateTest(){
// 发送 PUT 请求
WebClient.create(ADDRESS)
// 发送 json 格式数据
.type(MediaType.APPLICATION_JSON)
.put(new Student(), Student.class);
}
/**
* 删除测试
*/
@Test
public void deleteTest(){
// 发送 delete 请求
WebClient.create(ADDRESS+"/123")
.delete();
}
/**
* 查询所有测试
*/
@Test
public void findAllTest(){
// 发送 get 请求
List list = (List) WebClient.create(ADDRESS)
.getCollection(Student.class);
list.forEach(po -> {
System.out.println(po.getAge());
System.out.println(po.getName());
System.out.println(po.getBirthDay());
List bookList = po.getBookList();
bookList.forEach(po1 -> {
System.out.println(po1.getName());
System.out.println(po1.getPrice());
System.out.println(po1.getPublishDate());
});
});
}
@Test
public void findByIdTest(){
Student student = WebClient.create(ADDRESS+"/123")
.get(Student.class);
System.out.println(student);
}
}
```
## 遇到的坑
### 版本问题
cxf spring boot 启动器的版本要和 spring boot 版本对应,否则在启动时会报如下错误
[exception.png](./images/exception.png)
看 cxf 和 spring boot 的对应关系:https://mvnrepository.com/artifact/org.apache.cxf/cxf-spring-boot-starter-jaxws
[version.png](./images/version.png)
基本上用 cxf 最新的版本即可
### 传递 Date 类型
如果参数对象中存在 Date 类型属性,生成的客户端中是 XMLGregorianCalendar 类型,此时通过 Date 和 XMLGregorianCalendar 的转换可以解决。
```java
// Java program to Convert Date to XMLGregorianCalendar
// importing necessary packages
import java.util.Date;
import java.util.GregorianCalendar;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
public class DateToXMLGregorianCalendar {
public static void main(String[] args)
{
// Create Date Object
Date current_date = new Date();
// current date time in standard format
System.out.println("Standard Format :- "
+ current_date);
XMLGregorianCalendar xmlDate = null;
// Gregorian Calendar object creation
GregorianCalendar gc = new GregorianCalendar();
// giving current date and time to gc
gc.setTime(current_date);
try {
xmlDate = DatatypeFactory.newInstance()
.newXMLGregorianCalendar(gc);
}
catch (Exception e) {
e.printStackTrace();
}
// current date time in XMLGregorain Calendar format
System.out.println("XMLGregorianCalendar Format :- "
+ xmlDate);
}
}
```
### List参数没有 Setter 方法
如果参数对象中存在 List 对象属性,生成的客户端的参数对象中没有 List 对象属性的 Setter 方法,要设置 List 对象,
只能通过 `paramObject.getList().add()` 方法
### Restful 风格post请求要使用两个参数的方法
```java
// 发送 POST 请求
WebClient.create(ADDRESS)
.type(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.post(new Student(), Student.class);
```
最后如果使用 `.post(new Student());` 会报 HTTP 415 Unsupported Media Type
# 参考
https://www.geeksforgeeks.org/convert-date-to-xmlgregoriancalendar-in-java/
https://www.bilibili.com/video/BV1Kz4y1f78f?p=19