# 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