簡體   English   中英

自動關閉的Java代理(Jedis資源)

[英]Java proxy for Autocloseable (Jedis resources)

我試圖找出是否有可能創建Java動態代理來自動關閉Autocloseable資源,而不必記住用try-resources塊嵌入此類資源。

例如,我有一個JedisPool,它具有一個getResource方法,可以這樣使用:

try(Jedis jedis = jedisPool.getResource() {
   // use jedis client
}

現在,我做了這樣的事情:

class JedisProxy implements InvocationHandler {

    private final JedisPool pool;

    public JedisProxy(JedisPool pool) {
        this.pool = pool;
    }

    public static JedisCommands newInstance(Pool<Jedis> pool) {
        return (JedisCommands) java.lang.reflect.Proxy.newProxyInstance(
            JedisCommands.class.getClassLoader(),
            new Class[] { JedisCommands.class },
            new JedisProxy(pool));
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try (Jedis client = pool.getResource()) {
            return method.invoke(client, args);
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        } catch (Exception e) {
            throw e;
        }
    }
}

現在,每次我在Jedis(JedisCommands)上調用方法時,此方法都會傳遞給代理,該代理從池中獲取新客戶端,執行該方法並將此資源返回到池中。

它工作正常,但是當我想在客戶端上執行多個方法時,則對於每個方法,資源都從池中獲取並再次返回(這可能很耗時)。 你有什么想法要改進嗎?

您最終將擁有自己的“事務管理器”,在該事務管理器中,通常會立即將對象返回到池中,但是如果您開始了“事務”,則在您“提交”之前,對象不會返回到池中。交易”。

由於使用了手工定制的機制,因此突然出現的使用try-with-resources的問題變成了實際問題。

嘗試使用資源專家:

  • 語言內置功能
  • 允許您附加catch塊,並且資源仍被釋放
  • 簡單,一致的語法,因此即使開發人員不熟悉它,他也會看到所有Jedis代碼被其包圍,並且(希望)認為“因此,這必須是使用此代碼的正確方法”

缺點:

  • 您需要記住使用它

您的建議專家(您可以告訴我是否忘記了什么):

  • 即使開發人員不關閉資源也自動關閉,從而防止資源泄漏

缺點:

  • 額外的代碼始終意味着在其中查找錯誤的額外地方
  • 如果您不創建“事務”機制,那么您可能會遭受性能[jr]edis (我對[jr]edis或您的項目不熟悉,所以我不能說這是否真的是個問題)
  • 如果您確實創建了它,那么您將擁有更多易於出錯的額外代碼。
  • 語法不再簡單,它將使任何參加該項目的人感到困惑
  • 異常處理變得更加復雜
  • 您將通過反思進行所有代理呼叫(一個小問題,但是,這是我的列表;)
  • 可能更多,取決於最終的實現方式

如果您認為我沒有提出正確的觀點,請告訴我。 否則,我的主張將仍然是“您有一個尋找問題的'解決方案'”。

我認為這沒有朝正確的方向發展。 畢竟,開發人員應該習慣於正確處理資源,並且在未使用try(…){}來處理可自動關閉的資源時,IDE /編譯器能夠發出警告。

但是,創建用於裝飾所有調用的代理以及添加一種整體裝飾一批多個動作的方法的任務具有一般性,因此,它具有一般性的解決方案:

class JedisProxy implements InvocationHandler {

    private final JedisPool pool;

    public JedisProxy(JedisPool pool) {
        this.pool = pool;
    }

    public static JedisCommands newInstance(Pool<Jedis> pool) {
        return (JedisCommands) java.lang.reflect.Proxy.newProxyInstance(
            JedisCommands.class.getClassLoader(),
            new Class[] { JedisCommands.class },
            new JedisProxy(pool));
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try (Jedis client = pool.getResource()) {
            return method.invoke(client, args);
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        }
    }
    public static void executeBatch(JedisCommands c, Consumer<JedisCommands> action) {
        InvocationHandler ih = Proxy.getInvocationHandler(c);
        if(!(ih instanceof JedisProxy))
            throw new IllegalArgumentException();
        try(JedisCommands actual=((JedisProxy)ih).pool.getResource()) {
            action.accept(actual);
        }
    }
    public static <R> R executeBatch(JedisCommands c, Function<JedisCommands,R> action){
        InvocationHandler ih = Proxy.getInvocationHandler(c);
        if(!(ih instanceof JedisProxy))
            throw new IllegalArgumentException();
        try(JedisCommands actual=((JedisProxy)ih).pool.getResource()) {
            return action.apply(actual);
        }
    }
}

請注意,從Pool<Jedis>JedisPool的類型轉換對我來說JedisPool很可疑,但是我沒有更改該代碼中的任何內容,因為我沒有這些類來進行驗證。

現在您可以像

JedisCommands c=JedisProxy.newInstance(pool);

c.someAction();// aquire-someaction-close

JedisProxy.executeBatch(c, jedi-> {
    jedi.someAction();
    jedi.anotherAction();
}); // aquire-someaction-anotherAction-close

ResultType foo = JedisProxy.executeBatch(c, jedi-> {
    jedi.someAction();
    return jedi.someActionReturningValue(…);
}); // aquire-someaction-someActionReturningValue-close-return the value

批處理執行需要實例作為代理,否則會引發異常,因為很明顯,此方法不能保證生命周期未知的未知實例的特定行為。

而且,開發人員現在必須了解代理和批處理執行功能,就像他們在不使用代理時必須了解資源和try(…){}語句一樣。 另一方面,如果不是這樣,則在不使用批處理方法的情況下調用代理上的多個方法時,它們會失去性能,而在實際的非代理中,如果不使用try(…){}來調用多個方法,則會使資源泄漏try(…){}資源...

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM