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的相关操作和功能有了更深的理解。