Springboot整合ShardingJdbc实现分库分表方案

一、创建库表

1、创建两个数据库

CREATE SCHEMA `shard_db_0` DEFAULT CHARACTER SET utf8 ;
CREATE SCHEMA `shard_db_1` DEFAULT CHARACTER SET utf8 ;

2、在每个数据库各创建三个分表

CREATE TABLE `tb_order_0` (
  `order_id` bigint(20) NOT NULL,
  `buyer_id` bigint(20) not null comment '买家ID',
  `seller_id` bigint(20) not null comment '卖家ID',
  `order_name` varchar(64) not NULL COMMENT '商品名称',
  `price` decimal(10,2) DEFAULT NULL COMMENT '商品价格',
  PRIMARY KEY (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `tb_order_1` (
  `order_id` bigint(20) NOT NULL,
  `buyer_id` bigint(20) not null comment '买家ID',
  `seller_id` bigint(20) not null comment '卖家ID',
  `order_name` varchar(64) not NULL COMMENT '商品名称',
  `price` decimal(10,2) DEFAULT NULL COMMENT '商品价格',
  PRIMARY KEY (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `tb_order_2` (
  `order_id` bigint(20) NOT NULL,
  `buyer_id` bigint(20) not null comment '买家ID',
  `seller_id` bigint(20) not null comment '卖家ID',
  `order_name` varchar(64) not NULL COMMENT '商品名称',
  `price` decimal(10,2) DEFAULT NULL COMMENT '商品价格',
  PRIMARY KEY (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

2、创建工程

1、引入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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.1</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>sharding-sphere-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>sharding-sphere-demo</name>
    <description>sharding-sphere-demo</description>
    <url/>
    <licenses>
        <license/>
    </licenses>
    <developers>
        <developer/>
    </developers>
    <scm>
        <connection/>
        <developerConnection/>
        <tag/>
        <url/>
    </scm>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.15</version>
        </dependency>

        <!-- MyBatis-Plus -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
            <version>3.5.7</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.shardingsphere/shardingsphere-jdbc-core-spring-boot-starter -->
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
            <version>5.2.1</version>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.3.1</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

2、创建po

@Builder
@Data
@TableName("tb_buyer")
public class TbBuyer {

    @TableId
    private Long buyerId;
    private String buyerName;
    private Boolean sex;
    private Integer age;

}
@Builder
@Data
@TableName("tb_order")
public class TbOrder {

    @TableId
    private Long orderId;
    private Long buyerId;
    private Long sellerId;
    private String orderName;
    private BigDecimal price;

}
@Builder
@Data
@TableName("tb_seller")
public class TbSeller {

    @TableId
    private Long sellerId;
    private String sellerName;
    private Boolean sex;
    private Integer age;

}

3、创建mapper

package com.example.shardingsphere.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.shardingsphere.po.TbBuyer;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface TbBuyerMapper extends BaseMapper<TbBuyer> {
    // 可以在这里定义自定义方法
}
package com.example.shardingsphere.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.shardingsphere.po.TbOrder;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface TbOrderMapper extends BaseMapper<TbOrder> {
    // 可以在这里定义自定义方法
}
package com.example.shardingsphere.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.shardingsphere.po.TbSeller;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface TbSellerMapper extends BaseMapper<TbSeller> {
    // 可以在这里定义自定义方法
}

4、创建controller控制器

package com.example.shardingsphere.web;

import com.example.shardingsphere.mapper.TbBuyerMapper;
import com.example.shardingsphere.mapper.TbOrderMapper;
import com.example.shardingsphere.mapper.TbSellerMapper;
import com.example.shardingsphere.po.TbOrder;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

@RestController
public class OrderController {

    @Resource
    private TbBuyerMapper tbBuyerMapper ;
    @Resource
    private TbSellerMapper tbSellerMapper ;
    @Resource
    private TbOrderMapper tbOrderMapper ;

    /**
     * 查询订单详情
     */
    @GetMapping("/order/info/{orderId}")
    public Map<String,Object> orderInfo (@PathVariable Long orderId){
        Map<String,Object> orderMap = new HashMap<>() ;
        TbOrder order = tbOrderMapper.selectById(orderId) ;
        if (order != null){
            orderMap.put("order",order) ;
            orderMap.put("buyer",tbBuyerMapper.selectById(order.getBuyerId())) ;
            orderMap.put("seller",tbSellerMapper.selectById(order.getSellerId())) ;
        }
        return orderMap ;
    }
}

5、用到的工具类(雪花算法生成关联表主键ID,测试用,无特殊意义)

package com.example.shardingsphere.provider;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.SystemUtils;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;

/**
 * 雪花算法
 *
 */
@Slf4j
@Component
public class SnowFlakeProvider implements ApplicationRunner {

    private long workerId;
    private Snowflake snowFlake;
    private long datacenterId;

    /**
     * 初始化方法,用于在类实例化后进行必要的设置。
     * 本方法主要用于确定WorkerId,这是一个在分布式系统中唯一标识当前节点的ID。
     * 它通过获取本地主机名或IP并转换为long值来实现。
     * 如果无法获取本地主机名或IP,或者转换过程中发生异常,将不设置workerId,可能导致后续ID生成失败。
     */
    @Override
    public void run(ApplicationArguments args) throws Exception {
        //初始化workId
        initWorkId();

        initDataCenterId();

        //初始化SnowflakeID生成器
        createSnowFlake(workerId, datacenterId);
    }

    /**
     * 根据IP Address 生成workId
     *
     * @return
     */
    private void initWorkId() {
        try {
            String hostAddress = Inet4Address.getLocalHost().getHostAddress();
            log.info("hostAddress========={}", hostAddress);
            int[] ints = StringUtils.toCodePoints(hostAddress);
            int sums = 0;
            for (int b : ints) {
                sums += b;
            }
            workerId= (long) (sums % 32);
        } catch (UnknownHostException e) {
            log.error("根据IP获取workId失败。", e);
            // 如果获取失败,则使用随机数备用
            workerId = RandomUtil.randomLong(0, 31);
        }
    }
    /**
     * 根据HostName 生成dataCenterId
     * @return
     */
    private void initDataCenterId() {
        String hostName = getHostName();
        log.info("hostName========={}", hostName);
        int[] ints = StringUtils.toCodePoints(hostName);
        int sums = 0;
        for (int i : ints) {
            sums += i;
        }
        datacenterId = (long) (sums % 32);
    }

    /**
     * 获取 hostName
     *   SystemUtils.getHostName() 在mac系统为空处理
     * @return
     */
    public static String getHostName() {
        //获取当前操作系统名称,例如:windows xp,linux 等
        String osName = System.getProperty("os.name");
        String hostName = null;
        if(!StringUtils.startsWithIgnoreCase(osName,"mac")){
            hostName = SystemUtils.getHostName();
        }else{
            try {
                hostName = InetAddress.getLocalHost().getHostName().toUpperCase();
            } catch (UnknownHostException e) {
                hostName = "N/A";
                log.error("获取 hostName错误:", e);
            }
        }
        return hostName;
    }

    /**
     * 初始化SnowflakeID生成器。
     * 使用指定的workerId和datacenterId创建SnowflakeID生成器实例。如果创建失败,将抛出异常。
     *
     * @param workerId 工作节点ID,用于标识当前节点。
     * @param datacenterId 数据中心ID,用于标识数据中心。
     * @throws IllegalArgumentException 如果Snowflake实例创建失败,则抛出此异常。
     * @throws RuntimeException 如果Snowflake实例创建过程中发生其他异常,则抛出此异常。
     */
    private Snowflake createSnowFlake(long workerId, long datacenterId) {
        try {
            this.snowFlake = IdUtil.createSnowflake(workerId, datacenterId);
            // 参数合法性检查
            if (null == snowFlake) {
                throw new IllegalArgumentException("Failed to create Snowflake instance. Check workerId and datacenterId.");
            }
            return snowFlake;
        } catch (Exception e) {
            log.error("创建Snowflake实例失败,异常:{}", e.getMessage());
            throw new RuntimeException("Initialization failed for Snowflake ID generator.", e);
        }
    }

    /**
     * 获取一个唯一的雪花ID。使用Snowflake算法生成ID,该算法由Twitter开源。
     * 具体来说,这个方法调用了Snowflake实例的nextId方法来获取一个唯一的长整型ID。
     * 使用synchronized关键字确保了这个方法在多线程环境下的线程安全,
     * 保证了ID的生成不会因为并发而产生重复或错乱。
     *
     * @return 生成的唯一长整型ID。
     */
    public synchronized long snowflakeId() {
        // 调用Snowflake实例的nextId方法获取唯一ID
        return this.snowFlake.nextId();
    }

    /**
     * 生成基于Snowflake算法的唯一ID。
     * <p>
     * 使用Snowflake算法生成唯一的分布式ID。该算法由Twitter提出,通过组合时间戳、工作机器ID和序列号来生成全局唯一的ID。
     * 具体结构如下:
     * - 1位符号位,用于区分正负,由于ID只能是正数,所以这个位始终为0。
     * - 41位时间戳,精确到毫秒,可以使用约69年。
     * - 10位工作机器ID,可以部署在1024个节点,包括5位数据中心ID和5位工作机器ID。
     * - 12位序列号,用于同一毫秒内生成的ID去重,每个节点每毫秒可以生成4096个ID。
     * <p>
     * 参数:
     * workerId - 工作机器ID,用于标识不同的工作机器或进程。
     * datacenterId - 数据中心ID,用于标识不同的数据中心。
     * <p>
     * 返回:
     * 一个长整型的唯一ID,根据Snowflake算法生成。
     */
    public synchronized long snowflakeId(long workerId, long datacenterId) {
        return createSnowFlake(workerId, datacenterId).nextId();
    }

}

6、测试用例

package com.example.shardingsphere;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.shardingsphere.mapper.TbOrderMapper;
import com.example.shardingsphere.po.TbOrder;
import com.example.shardingsphere.provider.SnowFlakeProvider;
import lombok.extern.slf4j.Slf4j;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

@Slf4j
public class ShardTest extends ShardingSphereDemoApplicationTests{

    @Autowired
    private SnowFlakeProvider snowFlakeProvider;

    @Autowired
    private TbOrderMapper tbOrderMapper ;

    /**
     * 写入100条数据
     */
    @Test
    public void testOrderInsert (){
        List<TbOrder> list = new ArrayList<>();
        for (int i=1 ; i<= 10 ; i++){
            TbOrder order = TbOrder.builder()
//                    .orderId(snowFlakeProvider.snowflakeId())
                    .buyerId(snowFlakeProvider.snowflakeId())
                    .sellerId(snowFlakeProvider.snowflakeId())
                    .orderName("订单"+ RandomUtil.randomInt(6))
                    .price(RandomUtil.randomBigDecimal().setScale(2, BigDecimal.ROUND_HALF_UP))
                    .build();
            list.add(order);
        }
        tbOrderMapper.insert(list);
    }

    @Test
    public void testOrderQuery (){
        TbOrder order = tbOrderMapper.selectById(5) ;
        Assert.assertNotNull(order);
        log.info("查询结果:"+ JSONUtil.toJsonStr(order));
    }

    @Test
    public void testOrderUpdate (){
        TbOrder order = tbOrderMapper.selectById(3) ;
        Assert.assertNotNull(order);
        order.setBuyerId(1l);
        order.setSellerId(3l);
        int count = tbOrderMapper.updateById(order) ;
        log.info("更新记录数:"+count);
    }

    @Test
    public void testOrderPage (){
        //分页参数
        Page<TbOrder> rowPage = new Page<>(1, 2);
        //queryWrapper组装查询where条件
        LambdaQueryWrapper<TbOrder> queryWrapper = new LambdaQueryWrapper<>();
        Page<TbOrder> page = tbOrderMapper.selectPage(rowPage, queryWrapper);
        log.info("分页查询结果:"+ JSONUtil.toJsonStr(page));
    }

}

7、yml配置

server:
  port: 8095

spring:
  application:
    name: dynamic-datasource-spring-boot-starter
  shardingsphere:
    mode:
      type: Standalone
      repository:
        type: JDBC
    database:
      name: db0
    # 数据源配置
    datasource:
      # 数据源名称,多数据源以逗号分隔
      names: db0,db1
      db0:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://xx:3306/shard_db_0?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
        username: xx
        password: xx
      db1:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://xx:3306/shard_db_1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
        username: xx
        password: xx
    # 分片规则配置
    rules:
      sharding:
        # 分片算法配置
        sharding-algorithms:
          database-inline:
            # 分片算法类型
            type: INLINE
            props:
              # 分片算法的行表达式(算法自行定义,此处为方便演示效果)
              algorithm-expression: db${order_id % 2}
          table-inline:
            # 分片算法类型
            type: INLINE
            props:
              # 分片算法的行表达式
              algorithm-expression: tb_order_${order_id % 3}
        tables:
          # 逻辑表名称
          tb_order:
            # 行表达式标识符可以使用 ${...} 或 $->{...},但前者与 Spring 本身的属性文件占位符冲突,因此在 Spring 环境中使用行表达式标识符建议使用 $->{...}
            actual-data-nodes: db${0..1}.tb_order_${0..2}
            # 分库策略
            database-strategy:
              standard:
                # 分片列名称
                sharding-column: order_id
                # 分片算法名称
                sharding-algorithm-name: database-inline
            # 分表策略
            table-strategy:
              standard:
                # 分片列名称
                sharding-column: order_id
                # 分片算法名称
                sharding-algorithm-name: table-inline
    # 属性配置
    props:
      # 展示修改以后的sql语句
      sql-show: true

mybatis-plus:
  type-aliases-package: com.example.dynamic.po
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  mapper-locations:
    - classpath:/mapper/*.xml

至此,一个支持分库分表的工程搭建完成。

########################################################

关于搭建过程中遇到的问题(shardingsphere-jdbc-core-spring-boot-starter 5.2.1)

1、Caused by: java.lang.NoSuchMethodError: org.yaml.snakeyaml.representer.Representer: method 'void <init>()' not found

解答:该问题是由于版本问题导致的,是个兼容性问题。

shardingsphere-jdbc-core-spring-boot-starter 5.2.1中使用snakeyaml版本1.33 spring-boot-starter-web使用snakeyaml版本2.2 2.2中删除了Representer和SafeRepresenter的无参构造器,因此导致了该异常。

 可通过降低boot版本号从而降低其中snakeyaml版本号解决,但由于snakeyaml 1.x版本有安全问题,而shardingsphere-jdbc-core-spring-boot-starter已是当前最高版本,因此可通过重新jar中bean新增无参构造器去覆盖jar bean去解决。

com.main.java下新增目录org.yaml.snakeyaml.representer

目录下新建bean

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.yaml.snakeyaml.representer;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.TypeDescription;
import org.yaml.snakeyaml.DumperOptions.FlowStyle;
import org.yaml.snakeyaml.introspector.Property;
import org.yaml.snakeyaml.introspector.PropertyUtils;
import org.yaml.snakeyaml.nodes.MappingNode;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.NodeId;
import org.yaml.snakeyaml.nodes.NodeTuple;
import org.yaml.snakeyaml.nodes.ScalarNode;
import org.yaml.snakeyaml.nodes.SequenceNode;
import org.yaml.snakeyaml.nodes.Tag;

public class Representer extends SafeRepresenter {
    protected Map<Class<? extends Object>, TypeDescription> typeDefinitions = Collections.emptyMap();

    public Representer() {
        this.representers.put(null, new RepresentJavaBean());
    }

    public Representer(DumperOptions options) {
        super(options);
        this.representers.put(null, new RepresentJavaBean());
    }

    public TypeDescription addTypeDescription(TypeDescription td) {
        if (Collections.EMPTY_MAP == this.typeDefinitions) {
            this.typeDefinitions = new HashMap();
        }

        if (td.getTag() != null) {
            this.addClassTag(td.getType(), td.getTag());
        }

        td.setPropertyUtils(this.getPropertyUtils());
        return (TypeDescription)this.typeDefinitions.put(td.getType(), td);
    }

    public void setPropertyUtils(PropertyUtils propertyUtils) {
        super.setPropertyUtils(propertyUtils);
        Collection<TypeDescription> tds = this.typeDefinitions.values();
        Iterator var3 = tds.iterator();

        while(var3.hasNext()) {
            TypeDescription typeDescription = (TypeDescription)var3.next();
            typeDescription.setPropertyUtils(propertyUtils);
        }

    }

    protected MappingNode representJavaBean(Set<Property> properties, Object javaBean) {
        List<NodeTuple> value = new ArrayList(properties.size());
        Tag customTag = (Tag)this.classTags.get(javaBean.getClass());
        Tag tag = customTag != null ? customTag : new Tag(javaBean.getClass());
        MappingNode node = new MappingNode(tag, value, FlowStyle.AUTO);
        this.representedObjects.put(javaBean, node);
        DumperOptions.FlowStyle bestStyle = FlowStyle.FLOW;
        Iterator var8 = properties.iterator();

        while(true) {
            NodeTuple tuple;
            do {
                if (!var8.hasNext()) {
                    if (this.defaultFlowStyle != FlowStyle.AUTO) {
                        node.setFlowStyle(this.defaultFlowStyle);
                    } else {
                        node.setFlowStyle(bestStyle);
                    }

                    return node;
                }

                Property property = (Property)var8.next();
                Object memberValue = property.get(javaBean);
                Tag customPropertyTag = memberValue == null ? null : (Tag)this.classTags.get(memberValue.getClass());
                tuple = this.representJavaBeanProperty(javaBean, property, memberValue, customPropertyTag);
            } while(tuple == null);

            if (!((ScalarNode)tuple.getKeyNode()).isPlain()) {
                bestStyle = FlowStyle.BLOCK;
            }

            Node nodeValue = tuple.getValueNode();
            if (!(nodeValue instanceof ScalarNode) || !((ScalarNode)nodeValue).isPlain()) {
                bestStyle = FlowStyle.BLOCK;
            }

            value.add(tuple);
        }
    }

    protected NodeTuple representJavaBeanProperty(Object javaBean, Property property, Object propertyValue, Tag customTag) {
        ScalarNode nodeKey = (ScalarNode)this.representData(property.getName());
        boolean hasAlias = this.representedObjects.containsKey(propertyValue);
        Node nodeValue = this.representData(propertyValue);
        if (propertyValue != null && !hasAlias) {
            NodeId nodeId = nodeValue.getNodeId();
            if (customTag == null) {
                if (nodeId == NodeId.scalar) {
                    if (property.getType() != Enum.class && propertyValue instanceof Enum) {
                        nodeValue.setTag(Tag.STR);
                    }
                } else {
                    if (nodeId == NodeId.mapping && property.getType() == propertyValue.getClass() && !(propertyValue instanceof Map) && !nodeValue.getTag().equals(Tag.SET)) {
                        nodeValue.setTag(Tag.MAP);
                    }

                    this.checkGlobalTag(property, nodeValue, propertyValue);
                }
            }
        }

        return new NodeTuple(nodeKey, nodeValue);
    }

    protected void checkGlobalTag(Property property, Node node, Object object) {
        if (!object.getClass().isArray() || !object.getClass().getComponentType().isPrimitive()) {
            Class<?>[] arguments = property.getActualTypeArguments();
            if (arguments != null) {
                Class t;
                Iterator iter;
                Iterator var9;
                if (node.getNodeId() == NodeId.sequence) {
                    t = arguments[0];
                    SequenceNode snode = (SequenceNode)node;
                    Iterable<Object> memberList = Collections.emptyList();
                    if (object.getClass().isArray()) {
                        memberList = Arrays.asList((Object[])object);
                    } else if (object instanceof Iterable) {
                        memberList = (Iterable)object;
                    }

                    iter = ((Iterable)memberList).iterator();
                    if (iter.hasNext()) {
                        var9 = snode.getValue().iterator();

                        while(var9.hasNext()) {
                            Node childNode = (Node)var9.next();
                            Object member = iter.next();
                            if (member != null && t.equals(member.getClass()) && childNode.getNodeId() == NodeId.mapping) {
                                childNode.setTag(Tag.MAP);
                            }
                        }
                    }
                } else if (object instanceof Set) {
                    t = arguments[0];
                    MappingNode mnode = (MappingNode)node;
                    Iterator<NodeTuple> ite = mnode.getValue().iterator();
                    Set<?> set = (Set)object;
                    var9 = set.iterator();

                    while(var9.hasNext()) {
                        Object member = var9.next();
                        NodeTuple tuple = (NodeTuple)ite.next();
                        Node keyNode = tuple.getKeyNode();
                        if (t.equals(member.getClass()) && keyNode.getNodeId() == NodeId.mapping) {
                            keyNode.setTag(Tag.MAP);
                        }
                    }
                } else if (object instanceof Map) {
                    t = arguments[0];
                    Class<?> valueType = arguments[1];
                    MappingNode mnode = (MappingNode)node;
                    iter = mnode.getValue().iterator();

                    while(iter.hasNext()) {
                        NodeTuple tuple = (NodeTuple)iter.next();
                        this.resetTag(t, tuple.getKeyNode());
                        this.resetTag(valueType, tuple.getValueNode());
                    }
                }
            }

        }
    }

    private void resetTag(Class<? extends Object> type, Node node) {
        Tag tag = node.getTag();
        if (tag.matches(type)) {
            if (Enum.class.isAssignableFrom(type)) {
                node.setTag(Tag.STR);
            } else {
                node.setTag(Tag.MAP);
            }
        }

    }

    protected Set<Property> getProperties(Class<? extends Object> type) {
        return this.typeDefinitions.containsKey(type) ? ((TypeDescription)this.typeDefinitions.get(type)).getProperties() : this.getPropertyUtils().getProperties(type);
    }

    protected class RepresentJavaBean implements Represent {
        protected RepresentJavaBean() {
        }

        public Node representData(Object data) {
            return Representer.this.representJavaBean(Representer.this.getProperties(data.getClass()), data);
        }
    }
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.yaml.snakeyaml.representer;

import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import java.util.UUID;
import java.util.regex.Pattern;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.DumperOptions.FlowStyle;
import org.yaml.snakeyaml.DumperOptions.NonPrintableStyle;
import org.yaml.snakeyaml.DumperOptions.ScalarStyle;
import org.yaml.snakeyaml.error.YAMLException;
import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.Tag;
import org.yaml.snakeyaml.reader.StreamReader;

class SafeRepresenter extends BaseRepresenter {
    protected Map<Class<? extends Object>, Tag> classTags;
    protected TimeZone timeZone = null;
    protected DumperOptions.NonPrintableStyle nonPrintableStyle;
    private static final Pattern MULTILINE_PATTERN = Pattern.compile("\n|\u0085|\u2028|\u2029");

    public SafeRepresenter() {
        this(new DumperOptions());
    }

    public SafeRepresenter(DumperOptions options) {
        if (options == null) {
            throw new NullPointerException("DumperOptions must be provided.");
        } else {
            this.nullRepresenter = new RepresentNull();
            this.representers.put(String.class, new RepresentString());
            this.representers.put(Boolean.class, new RepresentBoolean());
            this.representers.put(Character.class, new RepresentString());
            this.representers.put(UUID.class, new RepresentUuid());
            this.representers.put(byte[].class, new RepresentByteArray());
            Represent primitiveArray = new RepresentPrimitiveArray();
            this.representers.put(short[].class, primitiveArray);
            this.representers.put(int[].class, primitiveArray);
            this.representers.put(long[].class, primitiveArray);
            this.representers.put(float[].class, primitiveArray);
            this.representers.put(double[].class, primitiveArray);
            this.representers.put(char[].class, primitiveArray);
            this.representers.put(boolean[].class, primitiveArray);
            this.multiRepresenters.put(Number.class, new RepresentNumber());
            this.multiRepresenters.put(List.class, new RepresentList());
            this.multiRepresenters.put(Map.class, new RepresentMap());
            this.multiRepresenters.put(Set.class, new RepresentSet());
            this.multiRepresenters.put(Iterator.class, new RepresentIterator());
            this.multiRepresenters.put((new Object[0]).getClass(), new RepresentArray());
            this.multiRepresenters.put(Date.class, new RepresentDate());
            this.multiRepresenters.put(Enum.class, new RepresentEnum());
            this.multiRepresenters.put(Calendar.class, new RepresentDate());
            this.classTags = new HashMap();
            this.nonPrintableStyle = options.getNonPrintableStyle();
            this.setDefaultScalarStyle(options.getDefaultScalarStyle());
            this.setDefaultFlowStyle(options.getDefaultFlowStyle());
        }
    }

    protected Tag getTag(Class<?> clazz, Tag defaultTag) {
        return this.classTags.containsKey(clazz) ? (Tag)this.classTags.get(clazz) : defaultTag;
    }

    public Tag addClassTag(Class<? extends Object> clazz, Tag tag) {
        if (tag == null) {
            throw new NullPointerException("Tag must be provided.");
        } else {
            return (Tag)this.classTags.put(clazz, tag);
        }
    }

    public TimeZone getTimeZone() {
        return this.timeZone;
    }

    public void setTimeZone(TimeZone timeZone) {
        this.timeZone = timeZone;
    }

    protected class RepresentUuid implements Represent {
        protected RepresentUuid() {
        }

        public Node representData(Object data) {
            return SafeRepresenter.this.representScalar(SafeRepresenter.this.getTag(data.getClass(), new Tag(UUID.class)), data.toString());
        }
    }

    protected class RepresentByteArray implements Represent {
        protected RepresentByteArray() {
        }

        public Node representData(Object data) {
            char[] binary = Base64Coder.encode((byte[])data);
            return SafeRepresenter.this.representScalar(Tag.BINARY, String.valueOf(binary), ScalarStyle.LITERAL);
        }
    }

    protected class RepresentEnum implements Represent {
        protected RepresentEnum() {
        }

        public Node representData(Object data) {
            Tag tag = new Tag(data.getClass());
            return SafeRepresenter.this.representScalar(SafeRepresenter.this.getTag(data.getClass(), tag), ((Enum)data).name());
        }
    }

    protected class RepresentDate implements Represent {
        protected RepresentDate() {
        }

        public Node representData(Object data) {
            Calendar calendar;
            if (data instanceof Calendar) {
                calendar = (Calendar)data;
            } else {
                calendar = Calendar.getInstance(SafeRepresenter.this.getTimeZone() == null ? TimeZone.getTimeZone("UTC") : SafeRepresenter.this.timeZone);
                calendar.setTime((Date)data);
            }

            int years = calendar.get(1);
            int months = calendar.get(2) + 1;
            int days = calendar.get(5);
            int hour24 = calendar.get(11);
            int minutes = calendar.get(12);
            int seconds = calendar.get(13);
            int millis = calendar.get(14);
            StringBuilder buffer = new StringBuilder(String.valueOf(years));

            while(buffer.length() < 4) {
                buffer.insert(0, "0");
            }

            buffer.append("-");
            if (months < 10) {
                buffer.append("0");
            }

            buffer.append(months);
            buffer.append("-");
            if (days < 10) {
                buffer.append("0");
            }

            buffer.append(days);
            buffer.append("T");
            if (hour24 < 10) {
                buffer.append("0");
            }

            buffer.append(hour24);
            buffer.append(":");
            if (minutes < 10) {
                buffer.append("0");
            }

            buffer.append(minutes);
            buffer.append(":");
            if (seconds < 10) {
                buffer.append("0");
            }

            buffer.append(seconds);
            if (millis > 0) {
                if (millis < 10) {
                    buffer.append(".00");
                } else if (millis < 100) {
                    buffer.append(".0");
                } else {
                    buffer.append(".");
                }

                buffer.append(millis);
            }

            int gmtOffset = calendar.getTimeZone().getOffset(calendar.getTime().getTime());
            if (gmtOffset == 0) {
                buffer.append('Z');
            } else {
                if (gmtOffset < 0) {
                    buffer.append('-');
                    gmtOffset *= -1;
                } else {
                    buffer.append('+');
                }

                int minutesOffset = gmtOffset / '\uea60';
                int hoursOffset = minutesOffset / 60;
                int partOfHour = minutesOffset % 60;
                if (hoursOffset < 10) {
                    buffer.append('0');
                }

                buffer.append(hoursOffset);
                buffer.append(':');
                if (partOfHour < 10) {
                    buffer.append('0');
                }

                buffer.append(partOfHour);
            }

            return SafeRepresenter.this.representScalar(SafeRepresenter.this.getTag(data.getClass(), Tag.TIMESTAMP), buffer.toString(), ScalarStyle.PLAIN);
        }
    }

    protected class RepresentSet implements Represent {
        protected RepresentSet() {
        }

        public Node representData(Object data) {
            Map<Object, Object> value = new LinkedHashMap();
            Set<Object> set = (Set)data;
            Iterator var4 = set.iterator();

            while(var4.hasNext()) {
                Object key = var4.next();
                value.put(key, (Object)null);
            }

            return SafeRepresenter.this.representMapping(SafeRepresenter.this.getTag(data.getClass(), Tag.SET), value, FlowStyle.AUTO);
        }
    }

    protected class RepresentMap implements Represent {
        protected RepresentMap() {
        }

        public Node representData(Object data) {
            return SafeRepresenter.this.representMapping(SafeRepresenter.this.getTag(data.getClass(), Tag.MAP), (Map)data, FlowStyle.AUTO);
        }
    }

    protected class RepresentPrimitiveArray implements Represent {
        protected RepresentPrimitiveArray() {
        }

        public Node representData(Object data) {
            Class<?> type = data.getClass().getComponentType();
            if (Byte.TYPE == type) {
                return SafeRepresenter.this.representSequence(Tag.SEQ, this.asByteList(data), FlowStyle.AUTO);
            } else if (Short.TYPE == type) {
                return SafeRepresenter.this.representSequence(Tag.SEQ, this.asShortList(data), FlowStyle.AUTO);
            } else if (Integer.TYPE == type) {
                return SafeRepresenter.this.representSequence(Tag.SEQ, this.asIntList(data), FlowStyle.AUTO);
            } else if (Long.TYPE == type) {
                return SafeRepresenter.this.representSequence(Tag.SEQ, this.asLongList(data), FlowStyle.AUTO);
            } else if (Float.TYPE == type) {
                return SafeRepresenter.this.representSequence(Tag.SEQ, this.asFloatList(data), FlowStyle.AUTO);
            } else if (Double.TYPE == type) {
                return SafeRepresenter.this.representSequence(Tag.SEQ, this.asDoubleList(data), FlowStyle.AUTO);
            } else if (Character.TYPE == type) {
                return SafeRepresenter.this.representSequence(Tag.SEQ, this.asCharList(data), FlowStyle.AUTO);
            } else if (Boolean.TYPE == type) {
                return SafeRepresenter.this.representSequence(Tag.SEQ, this.asBooleanList(data), FlowStyle.AUTO);
            } else {
                throw new YAMLException("Unexpected primitive '" + type.getCanonicalName() + "'");
            }
        }

        private List<Byte> asByteList(Object in) {
            byte[] array = (byte[])in;
            List<Byte> list = new ArrayList(array.length);

            for(int i = 0; i < array.length; ++i) {
                list.add(array[i]);
            }

            return list;
        }

        private List<Short> asShortList(Object in) {
            short[] array = (short[])in;
            List<Short> list = new ArrayList(array.length);

            for(int i = 0; i < array.length; ++i) {
                list.add(array[i]);
            }

            return list;
        }

        private List<Integer> asIntList(Object in) {
            int[] array = (int[])in;
            List<Integer> list = new ArrayList(array.length);

            for(int i = 0; i < array.length; ++i) {
                list.add(array[i]);
            }

            return list;
        }

        private List<Long> asLongList(Object in) {
            long[] array = (long[])in;
            List<Long> list = new ArrayList(array.length);

            for(int i = 0; i < array.length; ++i) {
                list.add(array[i]);
            }

            return list;
        }

        private List<Float> asFloatList(Object in) {
            float[] array = (float[])in;
            List<Float> list = new ArrayList(array.length);

            for(int i = 0; i < array.length; ++i) {
                list.add(array[i]);
            }

            return list;
        }

        private List<Double> asDoubleList(Object in) {
            double[] array = (double[])in;
            List<Double> list = new ArrayList(array.length);

            for(int i = 0; i < array.length; ++i) {
                list.add(array[i]);
            }

            return list;
        }

        private List<Character> asCharList(Object in) {
            char[] array = (char[])in;
            List<Character> list = new ArrayList(array.length);

            for(int i = 0; i < array.length; ++i) {
                list.add(array[i]);
            }

            return list;
        }

        private List<Boolean> asBooleanList(Object in) {
            boolean[] array = (boolean[])in;
            List<Boolean> list = new ArrayList(array.length);

            for(int i = 0; i < array.length; ++i) {
                list.add(array[i]);
            }

            return list;
        }
    }

    protected class RepresentArray implements Represent {
        protected RepresentArray() {
        }

        public Node representData(Object data) {
            Object[] array = (Object[])data;
            List<Object> list = Arrays.asList(array);
            return SafeRepresenter.this.representSequence(Tag.SEQ, list, FlowStyle.AUTO);
        }
    }

    private static class IteratorWrapper implements Iterable<Object> {
        private final Iterator<Object> iter;

        public IteratorWrapper(Iterator<Object> iter) {
            this.iter = iter;
        }

        public Iterator<Object> iterator() {
            return this.iter;
        }
    }

    protected class RepresentIterator implements Represent {
        protected RepresentIterator() {
        }

        public Node representData(Object data) {
            Iterator<Object> iter = (Iterator)data;
            return SafeRepresenter.this.representSequence(SafeRepresenter.this.getTag(data.getClass(), Tag.SEQ), new IteratorWrapper(iter), FlowStyle.AUTO);
        }
    }

    protected class RepresentList implements Represent {
        protected RepresentList() {
        }

        public Node representData(Object data) {
            return SafeRepresenter.this.representSequence(SafeRepresenter.this.getTag(data.getClass(), Tag.SEQ), (List)data, FlowStyle.AUTO);
        }
    }

    protected class RepresentNumber implements Represent {
        protected RepresentNumber() {
        }

        public Node representData(Object data) {
            Tag tag;
            String value;
            if (!(data instanceof Byte) && !(data instanceof Short) && !(data instanceof Integer) && !(data instanceof Long) && !(data instanceof BigInteger)) {
                Number number = (Number)data;
                tag = Tag.FLOAT;
                if (number.equals(Double.NaN)) {
                    value = ".NaN";
                } else if (number.equals(Double.POSITIVE_INFINITY)) {
                    value = ".inf";
                } else if (number.equals(Double.NEGATIVE_INFINITY)) {
                    value = "-.inf";
                } else {
                    value = number.toString();
                }
            } else {
                tag = Tag.INT;
                value = data.toString();
            }

            return SafeRepresenter.this.representScalar(SafeRepresenter.this.getTag(data.getClass(), tag), value);
        }
    }

    protected class RepresentBoolean implements Represent {
        protected RepresentBoolean() {
        }

        public Node representData(Object data) {
            String value;
            if (Boolean.TRUE.equals(data)) {
                value = "true";
            } else {
                value = "false";
            }

            return SafeRepresenter.this.representScalar(Tag.BOOL, value);
        }
    }

    protected class RepresentString implements Represent {
        protected RepresentString() {
        }

        public Node representData(Object data) {
            Tag tag = Tag.STR;
            DumperOptions.ScalarStyle style = SafeRepresenter.this.defaultScalarStyle;
            String value = data.toString();
            if (SafeRepresenter.this.nonPrintableStyle == NonPrintableStyle.BINARY && !StreamReader.isPrintable(value)) {
                tag = Tag.BINARY;
                byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
                String checkValue = new String(bytes, StandardCharsets.UTF_8);
                if (!checkValue.equals(value)) {
                    throw new YAMLException("invalid string value has occurred");
                }

                char[] binary = Base64Coder.encode(bytes);
                value = String.valueOf(binary);
                style = ScalarStyle.LITERAL;
            }

            if (SafeRepresenter.this.defaultScalarStyle == ScalarStyle.PLAIN && SafeRepresenter.MULTILINE_PATTERN.matcher(value).find()) {
                style = ScalarStyle.LITERAL;
            }

            return SafeRepresenter.this.representScalar(tag, value, style);
        }
    }

    protected class RepresentNull implements Represent {
        protected RepresentNull() {
        }

        public Node representData(Object data) {
            return SafeRepresenter.this.representScalar(Tag.NULL, "null");
        }
    }
}

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值