1. 程式人生 > >SpringBoot整合CXF,實現Restful api 與 WebService api dao層使用Mybatis

SpringBoot整合CXF,實現Restful api 與 WebService api dao層使用Mybatis

1、本demo目的查詢學生資訊【為了方便沒有寫批量查詢,也就是說以下getAllStudents也是一個一個查詢【而且沒有測試該方法正確性,之所以寫只是為了規範,返回Student的集合Students也是一個獨立的實體,非必需,請忽略】

2、主要是網上好多不全,整合的時候各種坑,我就記錄一下。

一、老規矩,先搞個數據庫

命名demo【可自己修改】
/*
Navicat MySQL Data Transfer

Source Server         : root
Source Server Version : 50540
Source Host           : localhost:3306
Source Database       : demo

Target Server Type    : MYSQL
Target Server Version : 50540
File Encoding         : 65001

Date: 2018-03-23 16:21:20
*/

SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for `student`
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
  `id` int(10) NOT NULL,
  `name` varchar(100) DEFAULT NULL,
  `sex` char(6) DEFAULT NULL,
  `address` varchar(255) DEFAULT NULL,
  `age` int(10) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES ('1', '李斯', '男', '江西南昌', '22');
ps【忽略細節,只是為了說明問題】

二、maven依賴【可能不是最少依賴】

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>cn.edu.jxnu</groupId>
	<artifactId>demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>demo</name>
	<description>Demo project for Spring Boot</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.10.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<!-- 提供JacksonJsonProvider,非必需,學過jersey,所以在這塊直接使用了,可以使用其他json轉化替換,使用cxf的有很多問題,沒法填 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jersey</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.apache.cxf</groupId>
			<artifactId>cxf-spring-boot-starter-jaxrs</artifactId>
			<version>3.1.11</version>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>1.3.1</version>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.7</version>
		</dependency>
		<dependency>
			<groupId>org.jsoup</groupId>
			<artifactId>jsoup</artifactId>
			<version>1.9.2</version>
		</dependency>
		<dependency>
			<groupId>org.apache.cxf</groupId>
			<artifactId>cxf-rt-frontend-jaxws</artifactId>
			<version>3.1.6</version>
		</dependency>
		<dependency>
			<groupId>org.apache.cxf</groupId>
			<artifactId>cxf-rt-transports-http</artifactId>
			<version>3.1.6</version>
		</dependency>
		<dependency>
			<groupId>org.apache.cxf</groupId>
			<artifactId>cxf-rt-transports-http-jetty</artifactId>
			<version>3.1.6</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>


</project>

三、實體類

package cn.edu.jxnu.entity;

import java.io.Serializable;

import javax.xml.bind.annotation.XmlRootElement;

/**
 * 學生實體類
 * 
 * @author: liguobin
 * @Description:
 * @時間: 2018-3-7 下午3:42:04
 * @version: V1.0
 * 
 */
@XmlRootElement(name = "Student")
public class Student implements Serializable {
	private static final long serialVersionUID = 1L;
	private Integer id;
	private String name;
	private char sex;
	private String address;
	private Integer age;

	public Student() {
		super();
	}

	public Student(Integer id, String name, char sex, String address,
			Integer age) {
		super();
		this.id = id;
		this.name = name;
		this.sex = sex;
		this.address = address;
		this.age = age;
	}

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public char getSex() {
		return sex;
	}

	public void setSex(char sex) {
		this.sex = sex;
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "Student [id=" + id + ", name=" + name + ", sex=" + sex
				+ ", address=" + address + ", age=" + age + "]";
	}

}
學生集合實體
package cn.edu.jxnu.entity;

import java.util.List;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

/**
 * 封裝多個學生實體類
 * 
 * @author: liguobin
 * @Description:
 * @時間: 2018-3-7 下午3:42:14
 * @version: V1.0
 * 
 */
@XmlRootElement(name = "Students")
public class Students {

	private List<Student> students;

	public Students(List<Student> students) {
		super();
		this.students = students;
	}

	@XmlElement(name = "Student")
	public List<Student> getStudents() {
		return students;
	}

	public Students() {
		super();
	}

	public void setStudents(List<Student> students) {
		this.students = students;
	}

}

四、開始整合SpringBoot mybatis

整合myabtis的具體不再贅述,網上很多也很簡單。application.properties
#server port set
server.port: 8082
#mysql datasource set
spring.datasource.
spring.datasource.url=jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

mybatis.typeAliasesPackage=cn.edu.jxnu.entity
mybatis.mapperLocations=classpath\:mapper/*.xml
mybatis.config-location=classpath\:mybatis-config.xml
spring.jackson.serialization.indent_output =true
logging.level.org.springframework.web=WARN
#logging.file = C\:\\web\\temp\\log\\log.log
logging.level.org.springframework=WARN
logging.level.cn.edu.jxnu.dao=INFO
logging.level.cn.edu.jxnu.resource=WARN

mybatis-config.xml 【沒幹什麼】
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<!-- 在這裡僅僅為了開啟懶載入 -->
	<settings>
		<!-- 開啟延遲載入的開關 -->
		<setting name="lazyLoadingEnabled" value="true" />
		<!-- 將積極載入改為消極載入,即延遲載入 -->
		<setting name="aggressiveLazyLoading" value="false" />
		<setting name="cacheEnabled" value="true" />
	</settings>

</configuration>

StudentMapper.xml mapper檔案
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.edu.jxnu.dao.StudentDao">
	<resultMap type="cn.edu.jxnu.entity.Student" id="Student">
		<id property="id" column="id" />
		<result property="name" column="name" />
		<result property="sex" column="sex" />
		<result property="address" column="address" />
		<result property="age" column="age" />
	</resultMap>
	<!-- 定義欄位集合 -->
	<sql id="studentInformation">
		id,name,sex,address,age
	</sql>
	<select id="getStudentById" resultMap="Student" flushCache="true"
		parameterType="java.lang.Integer">
		select
		<include refid="studentInformation" />
		from student where id=#{id}
	</select>
	<!-- 重新整理間隔 -->
	<cache flushInterval="600000" />
</mapper>

mapper介面
package cn.edu.jxnu.dao;

import org.apache.ibatis.annotations.Param;

import cn.edu.jxnu.entity.Student;

/**
 * dao層資料操作介面
 * 
 * @author: liguobin
 * @Description:
 * @時間: 2018-3-7 下午3:41:43
 * @version: V1.0
 * 
 */
public interface StudentDao {

	Student getStudentById(@Param("id") Integer id);
}

配置SpringBoot掃描dao【這個也是主啟動類,學過SpringBoot應該都知道,不再贅述】
package cn.edu.jxnu;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;
import org.springframework.stereotype.Controller;

/**
 * 啟動類
 * 
 * @author: liguobin
 * @Description:
 * @時間: 2018-3-7 下午3:41:24
 * @version: V1.0
 * 
 */
@Controller
@MapperScan("cn.edu.jxnu.dao")
@SpringBootApplication
// / 沒有這個rest失效 只存在soap
@ImportResource(locations = { "classpath:cxf-config.xml" })
public class DemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}
}
cxf-config.xml很重要,等等再說

五、重點  整合cxf webService【注意,不是restful,而是webservice 基於soap的】

cxf配置類【因為是使用SpringBoot,所以使用類和註解配置】

package cn.edu.jxnu;

import javax.xml.ws.Endpoint;

import org.apache.cxf.Bus;
import org.apache.cxf.bus.spring.SpringBus;
import org.apache.cxf.jaxws.EndpointImpl;
import org.apache.cxf.transport.servlet.CXFServlet;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;

import cn.edu.jxnu.serviceImpl.StudentServiceImpl;

/**
 * 
 * @author: liguobin @Description: @時間: 2018-3-7 下午4:11:57 @version: V1.0
 * 
 */
@Configuration
public class CxfConfig {

	@Bean
	public ServletRegistrationBean newServlet() {
		return new ServletRegistrationBean(new CXFServlet(), "/cxf/*");
	}

	@Bean(name = Bus.DEFAULT_BUS_ID)
	public SpringBus springBus() {
		return new SpringBus();
	}

	/**
	 * @return
	 */
	@Bean
	@Qualifier("studentServiceImpl") // 注入webService
	public Endpoint endpoint(StudentRestfulServiceImpl studentServiceImpl) {
		EndpointImpl endpoint = new EndpointImpl(springBus(), studentServiceImpl);
		endpoint.publish("/webService");// 暴露webService api,用在資源訪問
		return endpoint;
	}

	@Bean("jsonProvider") // 構造一個json轉化bean,用於將student轉化為json,因為後面需要用這個bean配置json轉化,所以給他取個名
	public JacksonJsonProvider getJacksonJsonProvider() {
		return new JacksonJsonProvider();

	}
}
CXFServlet()是返回一個cxf Servlet,用來攔截cxf,這裡傳入攔截路徑/cxf/* 也就是訪問路徑是: ip:port/cxf/這裡填@path的值
此處為了說明我寫了一個Spring Controller
package cn.edu.jxnu;

import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import cn.edu.jxnu.entity.Student;
import cn.edu.jxnu.entity.Students;
import cn.edu.jxnu.service.StudentService;

import com.alibaba.fastjson.JSONObject;

/**
 * Spring 前端控制
 * 
 * 通過這個返回正確,作對比
 * 
 * @author: liguobin
 * @Description:
 * @時間: 2018-3-7 下午3:35:17
 * @version: V1.0
 * 
 */
@Controller
public class SpringController {

	@Autowired
	private StudentService studentService;

	@ResponseBody
	@Produces({ MediaType.APPLICATION_JSON + "charset='utf-8'" })
	@RequestMapping(value = "get/{id}", method = RequestMethod.GET)
	public String getStudent(@PathVariable("id") Integer id) {
		Student student = studentService.getStudent(id);
		Object json = JSONObject.toJSON(student);
		return json.toString();
	}

	/**
	 * @引數:{"ids":{"id":[1,2,3,4]
	 * 
	 * @param ids
	 * @return
	 */
	@ResponseBody
	@Produces({ MediaType.APPLICATION_JSON + "charset='utf-8'" })
	@Consumes({ MediaType.APPLICATION_JSON })
	@RequestMapping(value = "gets/{ids}", method = RequestMethod.GET)
	public String getAll(@PathVariable("ids") String ids) {
		Students students = studentService.getAllStudent(ids);
		Object json = JSONObject.toJSON(students);
		return json.toString();
	}
}

眾所周知springmvc的預設由dispatherServlet處理。所以這裡使用ip:port/get/1 即可獲取【@PathVariable 是Spring的 與PathParam 是JSR-RS的功能類似

此處只是為了說明cxf 的重要性。可以看到,沒有使用/cxf/ 就依然是由springmvc來處理的


編寫webservice 介面  基本是最簡了

package cn.edu.jxnu.service;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import cn.edu.jxnu.entity.Student;
import cn.edu.jxnu.entity.Students;

/**
 * @description WebService介面定義 soap
 * @author liguobin
 * 
 */
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) // 返回型別
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) // 請求型別
@WebService
public interface StudentService {

	/**
	 * 查詢一個學生
	 * 
	 * @param id
	 * @return
	 */
	@WebMethod
	public Student getStudent(@WebParam(name = "id") Integer id);

	/**
	 * 查詢多個學生
	 * 
	 * @param ids
	 * @return
	 */
	@WebMethod
	public Students getAllStudent(@WebParam(name = "ids") String ids);

}

實現service
package cn.edu.jxnu.serviceImpl;

import java.util.ArrayList;

import javax.jws.WebService;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import cn.edu.jxnu.dao.StudentDao;
import cn.edu.jxnu.entity.Student;
import cn.edu.jxnu.entity.Students;
import cn.edu.jxnu.service.StudentService;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

/**
 * 實現webservice介面,對外暴露 soap
 * 
 * @author: liguobin 
 * @Description: 
 * @時間: 2018-3-7 下午3:43:06 
 * @version: V1.0
 * 
 */
@Component//由Spring管理
@WebService(endpointInterface = "cn.edu.jxnu.service.StudentService") // webservice介面的全類名
public class StudentServiceImpl implements StudentService {

	/**
	 * 注入spring bean
	 */
	@Autowired
	private StudentDao studentDao;

	@Override
	public Student getStudent(Integer id) {
		return studentDao.getStudentById(id);
	}

	/**
	 * 沒有測試正確性,不是本文重點
	 */
	@Override
	public Students getAllStudent(String ids) {
		Students students = new Students(new ArrayList<Student>());
		// 得到json物件
		JSONObject json = JSONObject.parseObject(ids);
		// 獲取物件的id列表
		JSONArray sid = json.getJSONArray("id");

		for (int i = 0; i < sid.size(); i++) {
			Integer s = sid.getInteger(0);
			if (s != null) {
				students.getStudents().add(studentDao.getStudentById(s));
			} else {
				continue;
			}
		}
		return students;

	}

}

測試webservice

輸入http://localhost:8082/cxf    【檢視所以soap和restful api】

單獨檢視某一個soap api【http://localhost:8082/cxf/webService?wsdl】 在前文  endpoint.publish("/webService");// 就是這個webService

瀏覽器顯示:


點選wsdl


名稱空間等等都是預設的,沒有在webservice配置。也為了簡潔

六、整合Restful api【 RESTful services

首先,編寫rest一個介面

package cn.edu.jxnu.resource;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import cn.edu.jxnu.entity.Student;
import cn.edu.jxnu.entity.Students;

/**
 * @author: liguobin 
 * @Description: 
 * @時間: 2018-3-7 下午3:59:15 
 * @version: V1.0
 * 
 */
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public interface StudentInterface {

	/**
	 * @param id
	 * @return
	 */
	@GET
	@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
	@Path("/getone/{id:[0-9]{0,10}}") // 限制id只能是0~9的陣列 不超過10位
	public Student getStudent(@PathParam("id") Integer id);

	/**
	 * 查詢多個學生
	 * 
	 * @param ids
	 * @return
	 */

	@GET
	@Produces({ MediaType.APPLICATION_JSON })
	@Path("/getmany/{ids}")
	public Students getAllStudent(@PathParam("ids") String ids);
}

實現rest介面
package cn.edu.jxnu.resource;

import java.util.ArrayList;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.springframework.beans.factory.annotation.Autowired;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import cn.edu.jxnu.entity.Student;
import cn.edu.jxnu.entity.Students;
import cn.edu.jxnu.service.StudentService;

@Path("/")
public class StudentInterfaceImpl implements StudentInterface {

	@Autowired
	private StudentService studentService;

	// 獲取json
	@Override
	@GET
	@Path("/getjson/{id:[0-9]{0,10}}")
	@Produces({ MediaType.APPLICATION_JSON })
	public Student getStudent(@PathParam("id") Integer id) {
		return studentService.getStudent(id);
	}

	// 獲取xml
	@GET
	@Path("/getxml/{id}")
	@Produces({ MediaType.APPLICATION_XML })
	public Student getStudent2(@PathParam("id") Integer id) {
		return studentService.getStudent(id);
	}

	/**
	 * 返回students集合,此方法沒有測試,引數是一個json物件,該物件包含一個name為id的陣列
	 */
	@Override
	@GET
	@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
	@Path("/getmany/{ids}")
	public Students getAllStudent(@PathParam("ids") String ids) {
		Students students = new Students(new ArrayList<Student>());
		// 得到json物件
		JSONObject json = JSONObject.parseObject(ids);
		// 獲取物件的id列表
		JSONArray sid = json.getJSONArray("id");

		for (int i = 0; i < sid.size(); i++) {
			Integer s = sid.getInteger(0);
			if (s != null) {
				students.getStudents().add(studentService.getStudent(s));
			} else {
				continue;
			}
		}
		return students;

	}

}
寫法不標準甚至有錯不要在意,不是本文重點

rest介面和類都寫完,剩下就是配置了

前文,在主類曾經匯入過一個配置檔案,這個檔案就是專門用來配置cxf的【@ImportResource(locations = { "classpath:cxf-config.xml" })

cxf-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxrs="http://cxf.apache.org/jaxrs"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="   
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd   
       http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd   
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
	<!-- 將Bean託管給Spring -->
	<bean id="studentService" class="cn.edu.jxnu.resource.StudentInterfaceImpl">
	</bean>
	<!-- 配置需要暴露的Rest ful Service -->
	<jaxrs:server id="restContainer" address="/students"> <!-- 暴露restful api 類似於前文提到的webService【暴露soap】 即訪問的時候要加上這個address -->
		<jaxrs:serviceBeans>
			<!-- 相當於打包釋出服務 -->
			<ref bean="studentService" />
		</jaxrs:serviceBeans>
		<!-- 提供一個json轉化,沒有這個不能自動返回json jsonProvider就是前面@Bean生成的在CxfConfig -->
		<jaxrs:providers>
			<ref bean="jsonProvider" />
		</jaxrs:providers>
	</jaxrs:server>
</beans>  


檢視RestFul api

瀏覽器輸入http://localhost:8082/cxf/ 就可以檢視到多了 Available RESTful services:Endpoint address: http://localhost:8082/cxf/students

【因為soap和restful均是cxf處理】

最後測試restful請求

輸入http://localhost:8082/cxf/students/getjson/1   獲取json資料


輸入http://localhost:8082/cxf/students/getxml/1  獲取xml資料

github 原始碼:https://github.com/jxnu-liguobin/SpringBoot-CXF-demo

以上僅供參考