最近在实现一个纯接口系统的需求,因为是支付类接口,所以考虑搭一套分布式的框架,跟后台系统隔离开来。
其实以前一直有疑问,接口是怎么调用与传输接收数据的呢?首先我们要了解:客户端与服务器常用数据交换格式xml、json、html;传输一般用http协议或者RPC协议
首先说xml
记得以前调过webService接口,就找了网上的例子当时好像是用的cxf框架,然后用xml配置暴露的接口,接口提供方可以用已实现序列化接口的对象来接收,当然,一般接口的数据结构没有那么简单,那我们也可以相对应的在我们要接收数据的对象里 加List或者对象属性来实现。cxf框架会自动把xml数据转化为我们所需的对象,底层传输的实际上是通过http协议传输xml数据(可以写一个拦截器获取发送的xml文件)。
然后是json
xml传输数据的话一般是webSerivce,偏银行多一些;而Json则偏互联网多一些。暴露接口的话,我们可以写一个controller,用@RequestMapping("xxxx")来指定接口的调用地址;底层传输的都是json字符串,我们可以用String来接收,也可以用对象来接收(实现序列化接口),一般用的框架有阿里的fastjson跟springMvc自带的jackson
了解了如何调用和传输数据后,我们直接开始
首先附上一张框架的结构图(框架为开源框架,fork下来自己做了点调整,地址:https://gitee.com/jmdhappy/xxpay-master.git)
web包就是服务消费者,service包为服务提供者。消费者的controller里面只负责接收请求就可以了,具体的业务在服务提供者里面实现(common包跟dal包可以根据自己的选择放入web中或者单独拎出来),服务消费者跟服务提供者里都配有一个application.yml文件,其中消费者需要配置数据库连接,提供者因为只需要调用,正常不需要配置数据库;每个包里都有一个pom文件,最外层的父级pom设置packing为pom,其他Pom设置packing为jar,互相之间根据相互调用去引入依赖即可。附上各个Pom文件截图:
父级pom:
<?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>org.xxpay</groupId>
<artifactId>xxpay4dubbo</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<name>xxpay4dubbo</name>
<description>xxpay4dubbo</description>
<modules>
<module>xxpay-common</module>
<module>xxpay-dal</module>
<module>xxpay4dubbo-api</module>
<module>xxpay4dubbo-web</module>
<module>xxpay4dubbo-service</module>
</modules>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.6.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<springboot.version>1.5.6.RELEASE</springboot.version>
<springboot.dubbo.version>1.0.0</springboot.dubbo.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.dubbo.springboot</groupId>
<artifactId>spring-boot-starter-dubbo</artifactId>
<version>${springboot.dubbo.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${springboot.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
api包pom:
<?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>org.xxpay</groupId>
<artifactId>xxpay4dubbo-api</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<name>xxpay4dubbo-api</name>
<description>xxpay4dubbo-api</description>
<parent>
<groupId>org.xxpay</groupId>
<artifactId>xxpay4dubbo</artifactId>
<version>1.0.0</version>
</parent>
<properties>
<commons.beanutils.version>1.7.0</commons.beanutils.version>
</properties>
<dependencies>
<dependency>
<groupId>org.xxpay</groupId>
<artifactId>xxpay-common</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
</project>
service包pom:
<?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>org.xxpay</groupId>
<artifactId>xxpay4dubbo-service</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<name>xxpay4dubbo-service</name>
<description>xxpay4dubbo-service</description>
<parent>
<groupId>org.xxpay</groupId>
<artifactId>xxpay4dubbo</artifactId>
<version>1.0.0</version>
</parent>
<dependencies>
<dependency>
<groupId>org.xxpay</groupId>
<artifactId>xxpay4dubbo-api</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.xxpay</groupId>
<artifactId>xxpay-dal</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>io.dubbo.springboot</groupId>
<artifactId>spring-boot-starter-dubbo</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-pool</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--wx_pay-->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId>
<version>2.8.0</version>
</dependency>
<!--ali_pay-->
<dependency>
<groupId>com.alipay</groupId>
<artifactId>sdk</artifactId>
<version>1.5</version>
<scope>system</scope>
<systemPath>${basedir}/src/main/webapp/WEB-INF/lib/alipay-sdk-java20170818173712.jar</systemPath>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/webapp/WEB-INF/lib/</directory>
<targetPath>BOOT-INF/lib/</targetPath>
<includes>
<include>**/*.jar</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<targetPath>BOOT-INF/classes/</targetPath>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${springboot.version}</version>
</plugin>
</plugins>
</build>
</project>
web包pom:
<?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>org.xxpay</groupId>
<artifactId>xxpay4dubbo-web</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<name>xxpay4dubbo-web</name>
<description>xxpay4dubbo-web</description>
<parent>
<groupId>org.xxpay</groupId>
<artifactId>xxpay4dubbo</artifactId>
<version>1.0.0</version>
</parent>
<dependencies>
<dependency>
<groupId>org.xxpay</groupId>
<artifactId>xxpay4dubbo-api</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.xxpay</groupId>
<artifactId>xxpay-dal</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>io.dubbo.springboot</groupId>
<artifactId>spring-boot-starter-dubbo</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${springboot.version}</version>
</plugin>
</plugins>
</build>
</project>
其实在这里踩了一些坑,因为原框架作者的包结构跟现在的有所不同,导致我改结构的时候报了一些错,印象最深的就是,明明有包,但是找不到。原因有2:一可能是最外层的父pom没有声明子模块;二是子pom文件没有声明父级Pom(父Pom文件添加的依赖为公共依赖,父pom引入后,子pom不需要再次引入。)
接下来看一下我们要实现的接口的接口文档:
我们可以来分析下接口的请求数据,最外层是data和sign,那我们首先考虑应该是个Map<String,xx>去装,其中data里面,biz_content每个接口的数据格式不一样,也应该用一个Map<String,xx>去装数据,根据各个接口的不同,看定义Map<String,List>还是Map<String,String>,那我们确定了应该装数据的结构就是:
Map<String, Map<String, Map<String,xx>>>,我们考虑中间一层Map用对象代替,最里层Map声明为对象属性,那么我们看一下其中一个接口的业务参数
Pojo类应该是这样:
package com.yz.modular.system.model;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
@Getter
@Setter
public class RequestTest1 implements Serializable {
private String mchNo;
private String outTrantNo;
private String bizType;
private String signType;
private String timeStamp;
private Map<String,List> bizContent;
}
考虑到data中除开bizContent,其他都是公共的,可以提取出来作为接口基类,那么我们要定义装此接口数据的pojo类就成了
package com.yz.modular.system.model;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
@Getter
@Setter
public class RequestTest1 extends RequestBaseInfo implements Serializable {
private Map<String,List> bizContent;
}
package com.yz.modular.system.model;
import com.baomidou.mybatisplus.activerecord.Model;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
/**
* <p>
* 商户账户信息表
* </p>
*
* @author fjj123
* @since 2018-06-13
*/
@Getter
@Setter
public class RequestBaseInfo extends Model<RequestBaseInfo> implements Serializable {
private static final long serialVersionUID = 1L;
private String mchNo;
private String outTrantNo;
private String bizType;
private String signType;
private String timeStamp;
@Override
protected Serializable pkVal() {
return this.mchNo;
}
}
接下来就是调用接口:
package com.yz.system;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.yz.GunsApplication;
import com.yz.core.util.DateUtil;
import com.yz.modular.system.model.Accnt;
import com.yz.modular.system.model.RequestTest1;
import com.yz.modular.system.service.IHisAccntMchService;
import com.yz.modular.system.service.IMchAccntService;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = GunsApplication.class,webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@EnableAutoConfiguration
public class ExceptionTest {
@Autowired
private IMchAccntService mch;
@Autowired
private IHisAccntMchService iHisAccntMchService;
@Before
public void before() throws Exception{
}
@After
public void after() throws Exception {
}
@ResponseBody
@Test
public void getAjaxJson(){
//调用spost方法时传入的外网接口地址
String url = "http://localhost:3020/api/pay/query_accnt.htm";
//调用spost方法时传入的map数据
//map放data,sign; data为对象(转json字符串)--- map.put("data",data),map.put("sign",sign)
//data放mchNo,BizType....bizContent;bizContent为Map<String,List>---- data.setBizContent(bizContentmap),data.setMchNo(mchNo)...
//bizeContent放split_accnt_detail; split_accnt_detail为list----- bizContentmap.put("split_accnt_detail",list) list.add(entity)
//倒着比较好理解, 对象用list装,list用map装,map用对象装,对象再用map装
Map<String, Object> dataMap = new HashMap<>();
//具体接口定义的具体pojo
RequestTest1 request001 = new RequestTest1();
//具体接口所需业务参数map
Map<String,List> bizContentmap = new HashMap<>();
//具体接口所需业务参数主题
List<Accnt> accntList = new ArrayList<>();
Accnt accnt = null;
for(int i =0;i<10;i++){
accnt = new Accnt();
accnt.setAmount("");
accnt.setDispatchEvent("");
accnt.setDispatchType("");
accnt.setMchAccntNo("");
accnt.setOrderNo("");
accntList.add(accnt);
}
bizContentmap.put("split_accnt_detail",accntList);
request001.setBizContent(bizContentmap);
request001.setMchNo("");
request001.setBizType("");
request001.setOutTrantNo("");
request001.setSignType("");
request001.setTimeStamp(DateUtil.getAllTime());
String data = JSONUtil.toJsonStr(request001);
String token = "12e243a21f3y2w1h3s132";
String sign = SecureUtil.md5(data+"&"+request001.getTimeStamp()+token);
dataMap.put("data",data);
dataMap.put("sign",sign);
String str = HttpUtil.post(url,dataMap);
System.out.println(str);
}
}
模拟http请求的接口其实有很多工具类,可以网上找一找,然后自己写一下,这里用的是hutool工具包http://hutool.mydoc.io/?t=255570;调用方就完成了!