zoukankan      html  css  js  c++  java
  • 【统一接口调用的设计与实现】对象到报文的互换

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

     
    接口调用生命周期
        业务调用时,我们通常将接口接口数据按照一定的规范封装成报文或者参数,然后通过网络协议将对应的报文发送给对应的外围接口地址,外围接受到相关业务请求后,将内部处理结果,再通过约定的报文形式回传给接口调用方,整个过程如下图所示:
     
                        
     
           1)接口地址:对方提供的一个可以访问的URL地址,访问地址可以直接带一些系统级或者业务级参数
           2)请求数据:数据在消息传输过程中,首先选择通过表单参数POST/GET提交,当请求数据过大或者多变情况,我们可以将请求参数按照一定的数据格式进行封装为字符串,然后通过传输协议报文头直接传输,这里的数据格式有XML格式、Json格式等。
           3)响应数据:相应数据一般直接放入 请求响应的数据流中,获得相应字符流按照数据格式解析为对应的响应数据
           4)传输协议:一般分为:webservice,socket,http等多种形式,主流为http(s),本文主要也是基于http(s)进行实现
     
    统一接口调用设计
            上面简单介绍了业务调用生命周期以及一些要点与经验,接下来看一下业务系统具体接口调用的过程。在业务系统设计中,我们通常从基础架构,业务架构,数据架构多个层面去建设,以大拆小,求同存异,进行模块设计,让模块 职责分明,高内聚,易扩展,同时模块间耦合度尽量低,调用方式尽量统一,简单。而这里我将会从业务层与接口层进行描述。
            1)业务层只需要关注业务自身逻辑,只需要在需要调用接口时调用一下接口层的API接口,调用API的数据需要是自己好获得、好理解,API调用简单明了,比如这里传入业务实体对象,而业务实体对象有接口层提供
            2)接口层不关心业务规则与流程,只需关注接口调用的规则以及其他细节,同时高扩展性,如:加解密、签名,报文封装,报文转换,并负责系统交互
            3)接口调用API需要简单、通用、易扩展特性
     
                
     
            业务层的接口调用,将相当于调用自己系统其他模块一样,底层调用是透明的。
            接下来我们开始讨论本文的重点,对象到报文的无缝转换
     
    对象到报文的互换工具类
            对象到报文的互换这里指的意思为接口层将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
    1. 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获得类对象
    1.  /**
           * 通过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
    1. {
        "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)验证类文件
      
    1. 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)调用代码生成器
    1.  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>
     
     通过!~
     
     
  • 相关阅读:
    web测试用例表(自用)
    程序员技术练级攻略
    整理:Google jQuery 引用地址大全和方法(转)
    开发神器之--Sublime Text
    Intellij编译时报“java: System Java Compiler was not found in classpath” 解决办法
    JAVA编译异常处理:java.lang.OutOfMemoryError: PermGen space
    mongo中查询Array类型的字段中元素个数
    BigDecimal进行除法divide运算注意事项
    用来代替本机IP的万能IP:127.0.0.1
    oracle中sys和System的默认密码
  • 原文地址:https://www.cnblogs.com/hframe/p/5152088.html
Copyright © 2011-2022 走看看