public <T> void putNode(final String uriPath, final T source, final TrieCacheTypeEnum cacheType) {
        if (StringUtils.isBlank(uriPath)) {
            return;
        }
        String strippedPath = StringUtils.strip(uriPath, "/");
        String[] pathParts = StringUtils.split(strippedPath, "/");
        if (ArrayUtils.isEmpty(pathParts)) {
            return;
        }
        if (TrieMatchModeEnum.PATH_PATTERN.equals(matchMode)) {
            checkLegalPath(uriPath, pathParts);
        }
        RuleData ruleData = null;
        SelectorData selectorData = null;
        ShenyuTrieNode node;
        if (TrieCacheTypeEnum.RULE.equals(cacheType)) {
            ruleData = (RuleData) source;
            node = keyRootMap.computeIfAbsent(ruleData.getSelectorId(), key -> new ShenyuTrieNode("/", "/", false));
        } else {
            selectorData = (SelectorData) source;
            node = keyRootMap.computeIfAbsent(selectorData.getPluginName(), key -> new ShenyuTrieNode("/", "/", false));
        }
        for (int i = 0; i < pathParts.length; i++) {
            node = putNode0(pathParts[i], node);
            if (Objects.isNull(node)) {
                remove(StringUtils.join(pathParts, "/", 0, i), source, cacheType);
                return;
            }
        }
        // after insert node, set full path and end of path
        node.setFullPath(uriPath);
        node.setEndOfPath(true);
        if (Objects.isNull(node.getPathCache())) {
            node.setPathRuleCache(new ConcurrentHashMap<>(Constants.TRIE_PATH_CACHE_SIZE));
        }
        if (TrieCacheTypeEnum.RULE.equals(cacheType)) {
            List<?> collections = node.getPathCache().get(ruleData.getSelectorId());
            if (CollectionUtils.isNotEmpty(collections)) {
                // synchronized list
                List<RuleData> ruleDataList = ListUtil.castList(collections, RuleData.class::cast);
                synchronized (ruleData.getId()) {
                    ruleDataList.add(ruleData);
                    ruleDataList.sort(Comparator.comparing(RuleData::getSort));
                    node.getPathCache().put(ruleData.getSelectorId(), ruleDataList);
                }
            } else {
                node.getPathCache().put(ruleData.getSelectorId(), Lists.newArrayList(ruleData));
            }
            node.setBizInfo(ruleData.getSelectorId());
            buildFailToNode(keyRootMap.get(ruleData.getSelectorId()));
        } else {
            List<?> collections = node.getPathCache().get(selectorData.getPluginName());
            if (CollectionUtils.isNotEmpty(collections)) {
                // synchronized list
                List<SelectorData> selectorDataList = ListUtil.castList(collections, SelectorData.class::cast);
                synchronized (selectorData.getId()) {
                    selectorDataList.add(selectorData);
                    selectorDataList.sort(Comparator.comparing(SelectorData::getSort));
                    node.getPathCache().put(selectorData.getPluginName(), selectorDataList);
                }
            } else {
                node.getPathCache().put(selectorData.getPluginName(), Lists.newArrayList(selectorData));
            }
            node.setBizInfo(selectorData.getPluginName());
            buildFailToNode(keyRootMap.get(selectorData.getPluginName()));
        }
    }
put node to trie, shenyu trie support *, **, path, pathVariable parameters.<br>
<p>* means match 0 or more character</p>
<p>** means match 0 or more dictory directory</p>
<p>pathVariable maybe like {name}</p>

@param uriPath uri path
@param source rule data or selector data
@param cacheType cache type
@param <T> biz info type
@see org.springframework.util.AntPathMatcher
@see org.springframework.web.util.pattern.PathPattern
shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/trie/ShenyuTrie.java