Bootstrap

案例研究之聊聊 QLExpress 源码 (三)

目前源码分析的过程中,缺少非常详细的场景,只是简单的通读了源码,并对于整个源码有了大体模块和功能了解。如果需要具体的分析,可以有具体场景,因为看源码确实比较耗时间。

三、配置模块(config)

3.1、QLExpress运行策略

  • 预防空指针

  • 比较空对象

  • 禁止调用不安全的方法

3.1.1、UML类图

3.1.2、QLExpressRunStrategy源码

package com.ql.util.express.config;

import com.ql.util.express.exception.QLSecurityRiskException;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * ExpressRunner设置全局生效的配置,直接使用静态方法控制
 */
public class QLExpressRunStrategy {


    /**
     * 预防空指针
     */

    private static boolean avoidNullPointer = false;

    /**
     * 当空对象进行大小比较时,返回false, 例如 1 > null 和 null > 1都返回false
     */
    private static boolean compareNullLessMoreAsFalse = false;

    /**
     * 是否比较空对象小于或者大于都返回false
     * @return
     */
    public static boolean isCompareNullLessMoreAsFalse() {
        return compareNullLessMoreAsFalse;
    }

    public static void setCompareNullLessMoreAsFalse(boolean compareNullLessMoreAsFalse) {
        QLExpressRunStrategy.compareNullLessMoreAsFalse = compareNullLessMoreAsFalse;
    }

    public static boolean isAvoidNullPointer() {
        return avoidNullPointer;
    }

    public static void setAvoidNullPointer(boolean avoidNullPointer) {
        QLExpressRunStrategy.avoidNullPointer = avoidNullPointer;
    }


    /**
     * 禁止调用不安全的方法
     */
    private static boolean forbiddenInvokeSecurityRiskMethods = false;

    public static boolean isForbiddenInvokeSecurityRiskMethods() {
        return forbiddenInvokeSecurityRiskMethods;
    }

    public static void setForbiddenInvokeSecurityRiskMethods(boolean forbiddenInvokeSecurityRiskMethods) {
        QLExpressRunStrategy.forbiddenInvokeSecurityRiskMethods = forbiddenInvokeSecurityRiskMethods;
    }

    private static ListsecurityRiskMethods = new ArrayList();

    /**
     * 静态代码块初始化,不允许外部调用系统方法
     */
    static{
        securityRiskMethods.add(System.class.getName()+"."+"exit");//系统退出
        securityRiskMethods.add(Runtime.getRuntime().getClass().getName()+".exec");//运行脚本命令
    }

    public static void addSecurityRiskMethod(Class clazz, String methodName )
    {
        QLExpressRunStrategy.securityRiskMethods.add(clazz.getName()+"."+methodName);
    }

    public static void assertBlackMethod(Method m) throws QLSecurityRiskException {

        if(forbiddenInvokeSecurityRiskMethods && m!=null){
            if(securityRiskMethods.contains(m.getDeclaringClass().getName()+"."+m.getName())) {
                throw new QLSecurityRiskException("使用QLExpress调用了不安全的系统方法:" + m.toString());
            }
        }
    }

}

3.2、QLExpressTimer计时器

3.2.1、UML类图

3.2.2、QLExpressTimer源码

package com.ql.util.express.config;

import com.ql.util.express.exception.QLTimeOutException;

import java.sql.SQLTimeoutException;

/**
 * QLExpress 计时器
 * @author tianqiao@taobao.com
 * @since 2019/6/17 4:12 PM
 */
public class QLExpressTimer {
    /**
     * 需要计时器,默认不需要
     */
    private static ThreadLocal NEED_TIMER = new ThreadLocal(){
        @Override
        protected Boolean initialValue() {
            return false;
        }
    };
    /**
     * 超时时间 s级
     */
    private static ThreadLocal TIME_OUT_MILLIS = new ThreadLocal(){};
    /**
     * 开始时间
     */
    private static ThreadLocal START_TIME = new ThreadLocal(){};
    /**
     * 结束时间
     */
    private static ThreadLocal END_TIME = new ThreadLocal(){};



    /**
     * 设置计时器
     * @param timeoutMillis 超时时间
     */
    public static void setTimer(long timeoutMillis)
    {
        NEED_TIMER.set(true);
        TIME_OUT_MILLIS.set(timeoutMillis);
    }

    /**
     * 开始计时
     */
    public static void startTimer()
    {
        if(NEED_TIMER.get()) {
            long t = System.currentTimeMillis();
            START_TIME.set(t);
            END_TIME.set(t+TIME_OUT_MILLIS.get());
        }
    }


    /**
     * 断言是否超时
     * @throws QLTimeOutException
     */
    public static void assertTimeOut() throws QLTimeOutException {

        if(NEED_TIMER.get() && System.currentTimeMillis()>END_TIME.get()){
            throw new QLTimeOutException("运行QLExpress脚本的下一条指令将超过了限定时间:" + TIME_OUT_MILLIS.get() + "ms");
        }
    }

    public static boolean hasExpired()
    {
        if(NEED_TIMER.get() && System.currentTimeMillis()>END_TIME.get()){
            return true;
        }
        return false;
    }

    public static void reset()
    {
        if(NEED_TIMER.get()) {
            START_TIME.remove();
            END_TIME.remove();
            NEED_TIMER.remove();
            TIME_OUT_MILLIS.remove();
        }
    }
}

3.3、小结

本模块属于配置模块,实际上更多的是附属追加的功能,比如禁止系统指令策略,计时器。之后如果有需要追加的扩展策略可以在这个模块下面补充。开源框架借鉴的思想如下:

  • 对于系统运行或者已经定义的命令或者系统类禁止使用

securityRiskMethods.add(System.class.getName()+"."+"exit");//系统退出
        securityRiskMethods.add(Runtime.getRuntime().getClass().getName()+".exec");//运行脚本命令

  • 计时器的线程安全ThreadLocal的使用