当前位置: 首页>编程语言>正文

统一接口设计 java 统一数据接口

    在我们的日常业务系统开发过程中,随着业务的发展,我们经常需要与外围系统进行接口对接,用以获得对方的业务能力或者将自己的业务能力提供给对方,本文主要介绍外围系统的接口调用的介绍和统一调用的设计与实现。

 

接口调用生命周期

    业务调用时,我们通常将接口接口数据按照一定的规范封装成报文或者参数,然后通过网络协议将对应的报文发送给对应的外围接口地址,外围接受到相关业务请求后,将内部处理结果,再通过约定的报文形式回传给接口调用方,整个过程如下图所示:

 

                    

统一接口设计 java 统一数据接口,统一接口设计 java 统一数据接口_json,第1张

 

    1)接口地址:对方提供的一个可以访问的URL地址,访问地址可以直接带一些系统级或者业务级参数

       2)请求数据:数据在消息传输过程中,首先选择通过表单参数POST/GET提交,当请求数据过大或者多变情况,我们可以将请求参数按照一定的数据格式进行封装为字符串,然后通过传输协议报文头直接传输,这里的数据格式有XML格式、Json格式等。

       3)响应数据:相应数据一般直接放入 请求响应的数据流中,获得相应字符流按照数据格式解析为对应的响应数据

       4)传输协议:一般分为:webservice,socket,http等多种形式,主流为http(s),本文主要也是基于http(s)进行实现

统一接口调用设计

        上面简单介绍了业务调用生命周期以及一些要点与经验,接下来看一下业务系统具体接口调用的过程。在业务系统设计中,我们通常从基础架构,业务架构,数据架构多个层面去建设,以大拆小,求同存异,进行模块设计,让模块 职责分明,高内聚,易扩展,同时模块间耦合度尽量低,调用方式尽量统一,简单。而这里我将会从业务层与接口层进行描述。

        1)业务层只需要关注业务自身逻辑,只需要在需要调用接口时调用一下接口层的API接口,调用API的数据需要是自己好获得、好理解,API调用简单明了,比如这里传入业务实体对象,而业务实体对象有接口层提供

        2)接口层不关心业务规则与流程,只需关注接口调用的规则以及其他细节,同时高扩展性,如:加解密、签名,报文封装,报文转换,并负责系统交互

        3)接口调用API需要简单、通用、易扩展特性

 

            

统一接口设计 java 统一数据接口,统一接口设计 java 统一数据接口_List_02,第2张

 

        业务层的接口调用,将相当于调用自己系统其他模块一样,底层调用是透明的。

        接下来我们开始讨论本文的重点,对象到报文的无缝转换

 

对象到报文的互换工具类

        对象到报文的互换这里指的意思为接口层将API接收到的业务对象转化为接口规范要求的接口报文(JSON/XML格式),这里我们我们选择借助第三方现成的jar包,我们只需要在其上做一个简单封装即可。

        json我们选择:jackson-core-2.2.3.jar,jackson-annotations-2.2.3.jar

        xml我们选择:xstream-1.4.7.jar

       封装我们的底层转换类:

     1)JsonUtils.java

public class JsonUtils {
    private static ObjectMapper objectMapper = new ObjectMapper();
    /**
     * Json内容转化为对象
     * @param content
     * @param valueType
     * @param <T>
     * @return
     * @throws IOException
     */
    public static  <T> T  readValue(String content, Class<T> valueType) throws IOException {
        return objectMapper.readValue(content,valueType);
    }
    /**
     * 对象转化为Json内容
     * @param t
     * @param <T>
     * @return
     * @throws IOException
     */
    public static  <T> String  writeValueAsString(T t) throws IOException {
        return objectMapper.writeValueAsString(t);
    }
}

 

      2) XmlUtils.java

public class XmlUtils {
    
    /**
     * XML内容转化为对象
     * @param content
     * @param valueType
     * @param <T>
     * @return
     */
    public static <T> T readValue(String content, Class<T> valueType) {
        XStream xstream = new XStream(new DomDriver());
        xstream.processAnnotations(valueType);
        return (T) xstream.fromXML(content);
    }
    /**
     * 对象转化为XML内容
     * @param t
     * @param <T>
     * @return
     * @throws IOException
     */
    public static  <T> String writeValueAsString(T t) throws IOException {
        XStream xstream = new XStream(new DomDriver("utf8"));
        xstream.processAnnotations(t.getClass());// 识别obj类中的注解
        // 以格式化的方式输出XML
        return xstream.toXML(t);
    }
}

        这样我们借助第三方jar包,我们轻松实现了这一步操作,而不用自己重复制造轮子!

 

 

通过报文模板实体类自动生成

        接口层需要向业务层提供java实体对象,我们常规的做法是根据接口规范进行逐一编写,当接口比较多且接口内容复杂时,难免感觉比较繁琐,大多数程序猿来并不希望自己来写这些无脑代码,于是这里提供另外一个工具类(BeanGeneratorUtil.java),根据数据报文示例生成实体类。

        通过Json获得类对象

/**
     * 通过Json数据生成Bean对象
     * @param packagePath
     * @param rootClassName
     * @param jsonString
     */
    public static void generateByJson(String packagePath, String rootClassName, String jsonString) throws IOException {
        JsonNode jsonNode = JacksonObjectMapper.getInstance().readTree(jsonString);
        Map<String, Object> mergeMap = new HashMap<String, Object>();
        mergeMap.put(rootClassName, parseJsonNode(jsonNode));
        generateClassByJson(packagePath, rootClassName, mergeMap.get(rootClassName), true);
    }
/**
     * 解析Json节点信息
     * @param jsonNode
     * @return
     */
    public static Object parseJsonNode(JsonNode jsonNode) {
        if(jsonNode.isArray()) {
            List result = new ArrayList();
            for (JsonNode subNode : jsonNode) {
                Map<String, Object> fieldMap = (Map<String, Object>)parseJsonNode(subNode);
                mergeField(result, fieldMap);
            }
            return result;
        }
        Map<String, Object> fieldMap = new LinkedHashMap<String, Object>();
        Iterator<Map.Entry<String, JsonNode>> fields = jsonNode.fields();
        while (fields.hasNext()) {
            Map.Entry<String, JsonNode> field = fields.next();
            fieldMap.put(field.getKey(), parseJsonNode(field.getValue()));
        }
        if(fieldMap.size() == 0) {
            return jsonNode.asText();
        }
        return fieldMap;
    }
/**
     * 生成类文件
     * @param packagePath
     * @param rootClassName
     * @param data
     * @param isRoot
     */
    private static void generateClassByJson(String packagePath, String rootClassName, Object data, boolean isRoot) {
        com.hframe.generator.bean.Class beanClass = new com.hframe.generator.bean.Class();
        beanClass.setSrcFilePath("E:\xfb_workspace\boomshare\bs-xfb-wx\src\main\java\");
        beanClass.setClassPackage(packagePath);
        beanClass.setClassName(rootClassName);
        beanClass.addConstructor();
        Map<String, Object> dataMap = new LinkedHashMap<String, Object>();
        if(data instanceof Map) {
            dataMap = (Map<String, Object>) data;
        }else if(data instanceof List){
            dataMap = (Map<String, Object>) ((List) data).get(0);
        }else {
            return ;
        }
        beanClass.addImportClass("com.fasterxml.jackson.annotation.JsonProperty");
        for (String fieldName : dataMap.keySet()) {
            Field field = getField(fieldName, dataMap.get(fieldName));
            field.addFieldAnno("@JsonProperty(\"" + fieldName + "\")");
            beanClass.addField(field);
            if(!"String".equals(field.getType())) {
                if(field.getType().startsWith("List<") && !beanClass.getImportClassList().contains("java.util.List")) {
                       beanClass.addImportClass("java.util.List");
                }
                if(isRoot) {
                    beanClass.addImportClass(packagePath + "." + CreatorUtil.getJavaClassName(rootClassName).toLowerCase() + ".*");
                }
                generateClassByJson(packagePath + (isRoot ? ("." + CreatorUtil.getJavaClassName(rootClassName).toLowerCase()) : ""),
                        CreatorUtil.getJavaClassName(fieldName), dataMap.get(fieldName), false);
            }
        }
        Map map = new HashMap();
        map.put("CLASS", beanClass);
        String content = VelocityUtil.produceTemplateContent("com/hframe/generator/vm/poByTemplate.vm", map);
        System.out.println(content);
        FileUtils.writeFile(beanClass.getFilePath(), content);
    }

 

 

   通过XML获得类对象

 

/**
     * 通过Xml数据生成Bean对象
     * @param packagePath
     * @param rootClassName
     * @param xmlString
     */
    public static void generateByXml(String packagePath, String rootClassName,String rootXmlName, String xmlString) throws IOException {
        Document document = Dom4jUtils.getDocumentByContent(xmlString);
        Element root = document.getRootElement();
        Map<String, Object> mergeMap = new HashMap<String, Object>();
        mergeMap.put(rootClassName, parseXmlNode(root));
        generateClassByXml(packagePath, rootClassName, rootXmlName, mergeMap.get(rootClassName), true);
    }
/**
     * 解析XML节点信息
     * @param element
     * @return
     */
    private static Object parseXmlNode(Element element) {
        if(checkElementIsArray(element)) {
            List result = new ArrayList();
            String xmlElementName = null;
            for (Object o : element.elements()) {
                Element subElement = (Element) o;
                xmlElementName = subElement.getName();//子元素名称
                Map<String, Object> fieldMap = (Map<String, Object>)parseXmlNode(subElement);
                mergeField(result, fieldMap);
            }
            result.add(xmlElementName);
            return result;
        }
        Map<String, Object> fieldMap = new LinkedHashMap<String, Object>();
        for (Object o : element.elements()) {
            Element subElement = (Element) o;
            fieldMap.put(subElement.getName(), parseXmlNode(subElement));
        }
        if(fieldMap.size() == 0) {
            return element.getTextTrim();
        }
        return fieldMap;
    }
/**
     * 生成类文件
     * @param packagePath
     * @param rootClassName
     * @param rootXmlName
     * @param data
     * @param isRoot
     */
    private static void generateClassByXml(String packagePath, String rootClassName,String rootXmlName, Object data, boolean isRoot) {
        com.hframe.generator.bean.Class beanClass = new com.hframe.generator.bean.Class();
        beanClass.setSrcFilePath("E:\xfb_workspace\boomshare\bs-xfb-wx\src\main\java\");
        beanClass.setClassPackage(packagePath);
        beanClass.setClassName(rootClassName);
        beanClass.addConstructor();
        beanClass.addAnnotation("@XStreamAlias(\"" + rootXmlName + "\")");
        Map<String, Object> dataMap = new LinkedHashMap<String, Object>();
        if(data instanceof Map) {
            dataMap = (Map<String, Object>) data;
        }else if(data instanceof List){
            dataMap = (Map<String, Object>) ((List) data).get(0);
        }else {
            return ;
        }
        beanClass.addImportClass("com.thoughtworks.xstream.annotations.XStreamAlias");
        beanClass.addImportClass("com.thoughtworks.xstream.annotations.XStreamAsAttribute");
        for (String fieldName : dataMap.keySet()) {
            String subElementName = getSubElementName(dataMap.get(fieldName));
            Field field = getField(fieldName, dataMap.get(fieldName), subElementName);
            field.addFieldAnno("@XStreamAlias(\"" + fieldName + "\")");
            beanClass.addField(field);
            if(!"String".equals(field.getType())) {
                if(field.getType().startsWith("List<") && !beanClass.getImportClassList().contains("java.util.List")) {
                    beanClass.addImportClass("java.util.List");
                }
                if(isRoot) {
                    beanClass.addImportClass(packagePath + "." + CreatorUtil.getJavaClassName(rootClassName).toLowerCase() + ".*");
                }
                if(subElementName != null) {
                    generateClassByXml(packagePath + (isRoot ? ("." + CreatorUtil.getJavaClassName(rootClassName).toLowerCase()) : ""),
                            CreatorUtil.getJavaClassName(subElementName), subElementName, dataMap.get(fieldName), false);
                }else {
                    generateClassByXml(packagePath + (isRoot ? ("." + CreatorUtil.getJavaClassName(rootClassName).toLowerCase()) : ""),
                            CreatorUtil.getJavaClassName(fieldName), fieldName, dataMap.get(fieldName), false);
                }
            }
        }
        Map map = new HashMap();
        map.put("CLASS", beanClass);
        String content = VelocityUtil.produceTemplateContent("com/hframe/generator/vm/poByTemplate.vm", map);
        System.out.println(content);
        FileUtils.writeFile(beanClass.getFilePath(), content);
    }

 

测试-Json

    1)准备测试报文-test.json

{
  "button":[
    {
      "type":"click",
      "name":"今日歌曲",
      "key":"V1001_TODAY_MUSIC"
    },
    {
      "name":"菜单",
      "sub_button":[
        {
          "type":"view",
          "name":"搜索",
          "url":"http://www.soso.com/"
        },
        {
          "type":"view",
          "name":"视频",
          "url":"http://v.qq.com/"
        },
        {
          "type":"click",
          "name":"赞一下我们",
          "key":"V1001_GOOD"
        }]
    }]
}

    2)调用代码生成器

public static void main(String[] args) throws IOException {
        String rootClassPath = Thread.currentThread().getContextClassLoader ().getResource("").getPath();
        String jsonString = FileUtils.readFile(rootClassPath + "test.json");
        generateByJson("com.wechat.bean.request","Menu",jsonString);
    }

 

 

   3)生成类文件

   Menu.java

   [menu]

      |-Button.java

      |-SubButton.java

        

代码如下:

@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
public class Menu   {
    @JsonProperty("button")
    private List<Button> buttonList;
    public Menu() {
        }
    public List<Button> getButtonList() {
        return buttonList;
    }
    public void setButtonList(List<Button> buttonList) {
        this.buttonList = buttonList;
    }
}
public class Button   {
    @JsonProperty("type")
    private String type;
    @JsonProperty("name")
    private String name;
    @JsonProperty("key")
    private String key;
    @JsonProperty("sub_button")
    private List<SubButton> subButtonList;
    public Button() {
        }
    
    public String getType(){
        return type;
    }
    public void setType(String type){
        this.type = type;
    }
     
    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name = name;
    }
     
    public String getKey(){
        return key;
    }
    public void setKey(String key){
        this.key = key;
    }
     
    public List<SubButton> getSubButtonList(){
        return subButtonList;
    }
    public void setSubButtonList(List<SubButton> subButtonList){
        this.subButtonList = subButtonList;
    }
}
public class SubButton   {
    @JsonProperty("type")
    private String type;
    @JsonProperty("name")
    private String name;
    @JsonProperty("url")
    private String url;
    @JsonProperty("key")
    private String key;
    public SubButton() {
    }
   
 
     
    public String getType(){
        return type;
    }
    public void setType(String type){
        this.type = type;
    }
     
    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name = name;
    }
     
    public String getUrl(){
        return url;
    }
    public void setUrl(String url){
        this.url = url;
    }
     
    public String getKey(){
        return key;
    }
    public void setKey(String key){
        this.key = key;
    }
}

 

 

   4)验证类文件

  

public static void main(String[] args) throws IOException {
        String rootClassPath = Thread.currentThread().getContextClassLoader ().getResource("").getPath();
        String jsonString = readFile(rootClassPath + "test.json");
        Menu menu = readValue(jsonString, Menu.class);
        System.out.println(writeValueAsString(menu));
    }

   5)输出结果

{"button":[{"type":"click","name":"今日歌曲","key":"V1001_TODAY_MUSIC","sub_button":null},{"type":null,"name":"菜单","key":null,"sub_button":[{"type":"view","name":"搜索","url":"http://www.soso.com/","key":null},{"type":"view","name":"视频","url":"http://v.qq.com/","key":null},{"type":"click","name":"赞一下我们","url":null,"key":"V1001_GOOD"}]}]}

 

        通过!

 

测试-XML

  1)准备测试报文-test.xml

    

<persons>
    <type>001</type>
    <listPerson>
        <person>
            <name>6666554</name>
            <sex>lavasoft</sex>
            <tel>man</tel>
            <addes>
                <address>
                    <addType>type1</addType>
                    <place>郑州市经三路财富广场1</place>
                </address>
                <address>
                    <addType>type2</addType>
                    <place>郑州市经三路财富广场2</place>
                </address>
            </addes>
        </person>
        <person>
            <name>7777754</name>
            <sex>yutian</sex>
            <tel>man</tel>
            <addes>
                <address>
                    <addType>type3</addType>
                    <place>郑州市经三路财富广场3</place>
                </address>
                <address>
                    <addType>type4</addType>
                    <place>郑州市经三路财富广场4</place>
                </address>
            </addes>
        </person>
    </listPerson>
</persons>

 

 2)调用代码生成器

String xmlString = FileUtils.readFile(rootClassPath + "test.xml");
 generateByXml("com.wechat.bean.request","Persons","persons",xmlString);

 

3)生成类文件

@XStreamAlias("persons")
public class Persons   {
    @XStreamAlias("type")
    private String type;
    @XStreamAlias("listPerson")
    private List<Person> personList;
    public Persons() {
        }
   
 
     
    public String getType(){
        return type;
    }
    public void setType(String type){
        this.type = type;
    }
     
    public List<Person> getPersonList(){
        return personList;
    }
    public void setPersonList(List<Person> personList){
        this.personList = personList;
    }
}
@XStreamAlias("person")
public class Person   {
    @XStreamAlias("name")
    private String name;
    @XStreamAlias("sex")
    private String sex;
    @XStreamAlias("tel")
    private String tel;
    @XStreamAlias("addes")
    private List<Address> addressList;
    public Person() {
        }
   
 
     
    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name = name;
    }
     
    public String getSex(){
        return sex;
    }
    public void setSex(String sex){
        this.sex = sex;
    }
     
    public String getTel(){
        return tel;
    }
    public void setTel(String tel){
        this.tel = tel;
    }
     
    public List<Address> getAddressList(){
        return addressList;
    }
    public void setAddressList(List<Address> addressList){
        this.addressList = addressList;
    }
}
@XStreamAlias("address")
public class Address   {
    @XStreamAlias("addType")
    private String addtype;
    @XStreamAlias("place")
    private String place;
    public Address() {
        }
   
 
     
    public String getAddtype(){
        return addtype;
    }
    public void setAddtype(String addtype){
        this.addtype = addtype;
    }
     
    public String getPlace(){
        return place;
    }
    public void setPlace(String place){
        this.place = place;
    }
}

 

 

  4)验证类文件

public static void main(String[] args) throws IOException {
        String rootClassPath = Thread.currentThread().getContextClassLoader ().getResource("").getPath();
        String xmlString = readFile(rootClassPath + "test.xml");
        System.out.println(xmlString);
        Persons person = readValue(xmlString, Persons.class);
        System.out.println(writeValueAsString(person));
    }

 

 5)输出结果

<persons>
    <type>001</type>
    <listPerson>
        <person>
            <name>6666554</name>
            <sex>lavasoft</sex>
            <tel>man</tel>
            <addes>
                <address>
                    <addType>type1</addType>
                    <place>郑州市经三路财富广场1</place>
                </address>
                <address>
                    <addType>type2</addType>
                    <place>郑州市经三路财富广场2</place>
                </address>
            </addes>
        </person>
        <person>
            <name>7777754</name>
            <sex>yutian</sex>
            <tel>man</tel>
            <addes>
                <address>
                    <addType>type3</addType>
                    <place>郑州市经三路财富广场3</place>
                </address>
                <address>
                    <addType>type4</addType>
                    <place>郑州市经三路财富广场4</place>
                </address>
            </addes>
        </person>
    </listPerson>
</persons>

 

 通过!~


https://www.xamrdz.com/lan/5x51923035.html

相关文章: