繁体   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