代码分析(十一)

2021SC@SDUSC
这次再来看那个至关重要的类

public abstract class AbstractParser<T> implements Parser<T>, ParserCreator<T>, VerifierCreator<T>, SQLCreator

抽象解析类,我们在上一次分析中已经将parser的前后转换,以及序列化和反序列化讲的很清楚,那么我们便来看这个类,这个类是实现了Parser、ParserCreator、VerifierCreator、SQLCreator这四个接口,各个接口的作用和功能也很明显,Parser是解析器,ParserCreator是解析器的创建,同理,VerifierCreator是验证器的创建,SQLCreator为SQL相关的创建器。

来看一下其中的代码方法parseCorrectRequest:

/**解析请求JSONObject
	 * @param request => URLDecoder.decode(request, UTF_8);
	 * @return
	 * @throws Exception 
	 */
	@NotNull
	public static JSONObject parseRequest(String request) throws Exception {
		JSONObject obj = JSON.parseObject(request);
		if (obj == null) {
			throw new UnsupportedEncodingException("JSON格式不合法!");
		}
		return obj;
	}

	@Override
	public JSONObject parseCorrectRequest(RequestMethod method, String tag, int version, String name, @NotNull JSONObject request
			, int maxUpdateCount, SQLCreator creator) throws Exception {

		if (RequestMethod.isPublicMethod(method)) {
			return request;//需要指定JSON结构的get请求可以改为post请求。一般只有对安全性要求高的才会指定,而这种情况用明文的GET方式几乎肯定不安全
		}

		if (StringUtil.isEmpty(tag, true)) {
			throw new IllegalArgumentException("请在最外层传 tag !一般是 Table 名,例如 \"tag\": \"User\" ");
		}

		//获取指定的JSON结构 <<<<<<<<<<<<
		JSONObject object = null;
		String error = "";
		try {
			object = getStructure("Request", method.name(), tag, version);
		} catch (Exception e) {
			error = e.getMessage();
		}
		if (object == null) { //empty表示随意操作  || object.isEmpty()) {
			throw new UnsupportedOperationException("找不到 version: " + version + ", method: " + method.name() + ", tag: " + tag + " 对应的 structure !"
					+ "非开放请求必须是后端 Request 表中校验规则允许的操作!\n " + error + "\n如果需要则在 Request 表中新增配置!");
		}

		JSONObject target = object;
		if (object.containsKey(tag) == false) { //tag 是 Table 名或 Table[]

			boolean isArrayKey = tag.endsWith(":[]");  //  JSONRequest.isArrayKey(tag);
			String key = isArrayKey ? tag.substring(0, tag.length() - 3) : tag;

			if (apijson.JSONObject.isTableKey(key)) {
				if (isArrayKey) { //自动为 tag = Comment:[] 的 { ... } 新增键值对 "Comment[]":[] 为 { "Comment[]":[], ... }
					target.put(key + "[]", new JSONArray()); 
				}
				else { //自动为 tag = Comment 的 { ... } 包一层为 { "Comment": { ... } }
					target = new JSONObject(true);
					target.put(tag, object);
				}
			}
		}

		//获取指定的JSON结构 >>>>>>>>>>>>>>


		//JSONObject clone 浅拷贝没用,Structure.parse 会导致 structure 里面被清空,第二次从缓存里取到的就是 {}
		return getVerifier().verifyRequest(method, name, target, request, maxUpdateCount, getGlobleDatabase(), getGlobleSchema(), creator);
	}

这里是判断了一下在object表里面是否有当前这个tag,如果没有的话,就去分析这个tag,如果这个tag以“:[]”结尾的话,那么就对他进行格式化,就是讲key赋值为tag的去除“"[]”部分,如果这个赋值后的key值是一个表的键值且是一个arraykey,那么就将此key值加上“[]”后,放入到我们的target里面,如果不是arraykey,那么就将target赋值一个新的JSONObject,然后再将此key值直接放入到target里面。

然后再来看一下如何获得Request或者Response内指定的结构:

	@Override
	public JSONObject getStructure(@NotNull String table, String method, String tag, int version) throws Exception  {

		String cacheKey = AbstractVerifier.getCacheKeyForRequest(method, tag);
		SortedMap<Integer, JSONObject> versionedMap = AbstractVerifier.REQUEST_MAP.get(cacheKey);

		JSONObject result = versionedMap == null ? null : versionedMap.get(Integer.valueOf(version));
		if (result == null) {  // version <= 0 时使用最新,version > 0 时使用 > version 的最接近版本(最小版本)
			Set<Entry<Integer, JSONObject>> set = versionedMap == null ? null : versionedMap.entrySet();

			if (set != null && set.isEmpty() == false) {
				Entry<Integer, JSONObject> maxEntry = null;

				for (Entry<Integer, JSONObject> entry : set) {
					if (entry == null || entry.getKey() == null || entry.getValue() == null) {
						continue;
					}

					if (version <= 0 || version == entry.getKey()) {  // 这里应该不会出现相等,因为上面 versionedMap.get(Integer.valueOf(version))
						maxEntry = entry;
						break;
					}

					if (entry.getKey() < version) {
						break;
					}

					maxEntry = entry;
				}

				result = maxEntry == null ? null : maxEntry.getValue();
			}

			if (result != null) {  // 加快下次查询,查到值的话组合情况其实是有限的,不属于恶意请求
				if (versionedMap == null) {
					versionedMap = new TreeMap<>((o1, o2) -> {
						return o2 == null ? -1 : o2.compareTo(o1);  // 降序
					});
				}

				versionedMap.put(Integer.valueOf(version), result);
				AbstractVerifier.REQUEST_MAP.put(cacheKey, versionedMap);
			}
		}

这一部分就是对版本的处理,其中包含了对性能的提升,大致就是通过加入时去进行一个排序从而加快下一次的查询。
同时根据这一段可以看到作者目前其实只使用 了Request 而没有使用 Response,所以这里写死只用了 REQUEST_MAP,所以如作者所说,可能在以后 Response 表也会与 Request 表合并,用字段来区分

		if (result == null) {
			if (AbstractVerifier.REQUEST_MAP.isEmpty() == false) {
				return null;  // 已使用 REQUEST_MAP 缓存全部,但没查到
			}

			//获取指定的JSON结构 <<<<<<<<<<<<<<
			SQLConfig config = createSQLConfig().setMethod(GET).setTable(table);
			config.setPrepared(false);
			config.setColumn(Arrays.asList("structure"));

			Map<String, Object> where = new HashMap<String, Object>();
			where.put("method", method);
			where.put(JSONRequest.KEY_TAG, tag);

			if (version > 0) {
				where.put(JSONRequest.KEY_VERSION + "{}", ">=" + version);
			}
			config.setWhere(where);
			config.setOrder(JSONRequest.KEY_VERSION + (version > 0 ? "+" : "-"));
			config.setCount(1);

			//too many connections error: 不try-catch,可以让客户端看到是服务器内部异常
			result = getSQLExecutor().execute(config, false);

			// version, method, tag 组合情况太多了,JDK 里又没有 LRUCache,所以要么启动时一次性缓存全部后面只用缓存,要么每次都查数据库
			//			versionedMap.put(Integer.valueOf(version), result);
			//			AbstractVerifier.REQUEST_MAP.put(cacheKey, versionedMap);
		}

		return getJSONObject(result, "structure"); //解决返回值套了一层 "structure":{}
	}

这个获取结构首先就是
JSONObject result = versionedMap == null ? null : versionedMap.get(Integer.valueOf(version));
就是获取到当前的version表里面是否含有此版本,如果没有则赋值为null,否则就获取到相应的版本所对应的值,而当result为null时,如果此version小于零,则使用最新的版本,而如果大于零,那么我们就选择大于此版本且最接近此版本的version来替代它,同时还要把此版本加入到我们的现有version表里面以加快下次的查询。接下来就是去装载config,我们主要就是将config的column赋值为“structure”,这样就可以通过

result = getSQLExecutor().execute(config, false);
return getJSONObject(result, "structure");

来获取到Request或者Response内指定的结构。


与小组成员讨论
在这次分析中,由于涉及到团队成员的SQLConfig类,所以我们进行了相关的交流,也都相互访问了对应的博客,并对我们两个在分析过程中的具体问题进行了讨论,比如config对象如何通过一个version和一个“structure”来获取到对应的数据结构,以及用getSQLExecutor()得到一个SOL的执行器之后调用execute(config, false)又会怎么进行,实际上就是去调用其父类fastjson里面的getJSONObject方法通过instanceof去获得相对应的值,我们在这次讨论中也都对apijson的具体机制,以及config的相关操作和功能有了更深的理解。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值