jFinal事務實現的原理

本文內容

  1. jFinal怎樣使用數據庫事務
  2. jFinal的事務是怎么實現的
  1. 在需要事務的方法上添加注解@Before(Tx.class),代碼如下
@Before(Tx.class)
public boolean updateOrder(Order order){
  .......
}

并且在實例化此類的時候實用動態代理來增強,代碼如下

private OrderDao orderDao = Enhancer.enhance(OrderDao.class);
  1. 從Enhancer類的enhance方法開始,enhance方法的代碼如下
public static <T> T enhance(Class<T> targetClass) {
    return (T)net.sf.cglib.proxy.Enhancer.create(targetClass, new Callback());
}

使用cglib的動態代理功能來增強目標類,通過回調Callback類中的intercept方法來調用被代理類的方法,intercept代碼如下

public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        if (excludedMethodName.contains(method.getName())) {
            if (method.getName().equals("finalize"))
                return methodProxy.invokeSuper(target, args);
            return this.injectTarget != null ? methodProxy.invoke(this.injectTarget, args) : methodProxy.invokeSuper(target, args);
        }
        
        if (this.injectTarget != null) {
            target = this.injectTarget;
            Interceptor[] finalInters = InterceptorBuilder.build(injectInters, target.getClass(), method);
            Invocation invocation = new Invocation(target, method, args, methodProxy, finalInters);
            invocation.useInjectTarget = true;
            invocation.invoke();
            return invocation.getReturnValue();
        }
        else {
            Interceptor[] finalInters = InterceptorBuilder.build(injectInters, target.getClass(), method);
            Invocation invocation = new Invocation(target, method, args, methodProxy, finalInters);
            invocation.useInjectTarget = false;
            invocation.invoke();
            return invocation.getReturnValue();
        }
    }

首先判斷方法名為“finalize”的邏輯不看,跳過
看下面if中的代碼

if (this.injectTarget != null) {
    target = this.injectTarget;
    // 獲取到所有的攔截器集合
    Interceptor[] finalInters = InterceptorBuilder.build(injectInters, target.getClass(), method);
    // 將攔截器交給Invocation
    Invocation invocation = new Invocation(target, method, args, methodProxy, finalInters);
    invocation.useInjectTarget = true;
    // 然后調用
    invocation.invoke();
    return invocation.getReturnValue();
}

第一步獲取到所有的攔截器集合,InterceptorBuilder.build的代碼如下

public static Interceptor[] build(Interceptor[] injectInters, Class<?> targetClass, Method method) {
        Interceptor[] methodInters = createInterceptors(method.getAnnotation(Before.class));
        
        // no Clear annotation
        Clear clear = method.getAnnotation(Clear.class);
        if (clear == null) {
            Interceptor[] classInters = createInterceptors(targetClass.getAnnotation(Before.class));
            Interceptor[] result = new Interceptor[globalInters.length + injectInters.length + classInters.length + methodInters.length];
            int index = 0;
            for (Interceptor inter : globalInters)
                result[index++] = inter;
            for (Interceptor inter : injectInters)
                result[index++] = inter;
            for (Interceptor inter : classInters)
                result[index++] = inter;
            for (Interceptor inter : methodInters)
                result[index++] = inter;
            return result;
        }
        
        // Clear annotation without parameter
        Class<? extends Interceptor>[] clearInters = clear.value();
        if (clearInters.length == 0)
            return methodInters;
        
        // Clear annotation with parameter
        Interceptor[] classInters = createInterceptors(targetClass.getAnnotation(Before.class));
        Interceptor[] temp = new Interceptor[globalInters.length + injectInters.length + classInters.length];
        int index = 0;
        for (Interceptor inter : globalInters)
            temp[index++] = inter;
        for (Interceptor inter : injectInters)
            temp[index++] = inter;
        for (Interceptor inter : classInters)
            temp[index++] = inter;
        
        int removeCount = 0;
        for (int i=0; i<temp.length; i++) {
            for (Class<? extends Interceptor> ci : clearInters) {
                if (temp[i].getClass() == ci) {
                    temp[i] = null;
                    removeCount++;
                    break;
                }
            }
        }       
        
        Interceptor[] result = new Interceptor[temp.length + methodInters.length - removeCount];
        index = 0;
        for (Interceptor inter : temp)
            if (inter != null)
                result[index++] = inter;
        for (Interceptor inter : methodInters)
            result[index++] = inter;
        return result;
    }

第二步將攔截器交給Invocation
第三步調用invocation.invoke(),看invoke的代碼

public void invoke() {
    // 遞歸調用攔截器
    if (index < inters.length) {
        // 將攔截器實例傳入一下攔截器
        inters[index++].intercept(this);
    }
    else if (index++ == inters.length) {    // index++ ensure invoke action only one time
        try {
            // Invoke the action
            if (action != null) {
                // 調用到具體Controller子類的方法
                returnValue = action.getMethod().invoke(target, args);
            }
            // Invoke the method
            else {
                if (useInjectTarget){
                    returnValue = methodProxy.invoke(target, args);
                }
                else{
                    returnValue = methodProxy.invokeSuper(target, args);
                }
            }
        }
        catch (InvocationTargetException e) {
            Throwable t = e.getTargetException();
            throw t instanceof RuntimeException ? (RuntimeException)t : new RuntimeException(e);
        }
        catch (RuntimeException e) {
            throw e;
        }
        catch (Throwable t) {
            throw new RuntimeException(t);
        }
    }
}

遞歸調用攔截器,并把自己的實例傳入進去。
最終會調用到Tx類中的intercept方法,因為Tx類就是一個攔截器,代碼如下

public void intercept(Invocation inv) {
    Config config = getConfigWithTxConfig(inv);
    if (config == null)
        config = DbKit.getConfig();
    
    Connection conn = config.getThreadLocalConnection();
    if (conn != null) { // Nested transaction support
        try {
            if (conn.getTransactionIsolation() < getTransactionLevel(config))
                conn.setTransactionIsolation(getTransactionLevel(config));
            inv.invoke();
            return ;
        } catch (SQLException e) {
            throw new ActiveRecordException(e);
        }
    }
    
    Boolean autoCommit = null;
    try {
        conn = config.getConnection();
        autoCommit = conn.getAutoCommit();
        config.setThreadLocalConnection(conn);
        conn.setTransactionIsolation(getTransactionLevel(config));  // conn.setTransactionIsolation(transactionLevel);
        conn.setAutoCommit(false);
        inv.invoke();
        conn.commit();
    } catch (NestedTransactionHelpException e) {
        if (conn != null) try {conn.rollback();} catch (Exception e1) {e1.printStackTrace();}
    } catch (Throwable t) {
        if (conn != null) try {conn.rollback();} catch (Exception e1) {e1.printStackTrace();}
        throw t instanceof RuntimeException ? (RuntimeException)t : new ActiveRecordException(t);
    }
    finally {
        try {
            if (conn != null) {
                if (autoCommit != null)
                    conn.setAutoCommit(autoCommit);
                conn.close();
            }
        } catch (Throwable t) {
            t.printStackTrace();    // can not throw exception here, otherwise the more important exception in previous catch block can not be thrown
        }
        finally {
            config.removeThreadLocalConnection();   // prevent memory leak
        }
    }
}

1)從config中獲取連接對象conn,保留連接事務的開關
2)把conn連接對象交給本地線程保管,確保下游數據庫操作獲取的conn對象是同一個 3)配置事務級別
4)設置手動提交事務
5)inv.invoke();這一行代碼會調用下一個攔截器,最終調用到上面invoke() 方法內的這幾行代碼,因為useInjectTarget是false,所以看else中的代碼

if (useInjectTarget){
    returnValue = methodProxy.invoke(target, args);
}
else{
    returnValue = methodProxy.invokeSuper(target, args);
}

這就執行了開頭介紹的方法,

@Before(Tx.class)
public boolean updateOrder(Order order){
  .......
}

方法內的數據庫操作完成后,繼續執行conn.commit();提交事務
6)如果有事務異常就回滾
7)還原連接的事務開關
8)從本地線程中移除連接
這就完成了事務操作

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,778評論 18 399
  • 背景 一年多以前我在知乎上答了有關LeetCode的問題, 分享了一些自己做題目的經驗。 張土汪:刷leetcod...
    土汪閱讀 12,776評論 0 33
  • MyBatis提供了一種插件(plugin)的功能,雖然叫做插件,但其實這是攔截器功能。那么攔截器攔截MyBati...
    七寸知架構閱讀 3,258評論 3 54
  • 生活中 很多時候做選擇時,我們都以為兩 條路只能選其一,殊不知,還有另外一條也 許兩全其美的路。 活了這么...
    是媛媛閱讀 173評論 0 0
  • 今天是大年初一,懷左在這里給大家拜年~希望新的一年,我們都越來越好~ 01 昨晚睡得比較晚,今早起床聽歌時,轉到了...
    懷左同學閱讀 1,431評論 11 36