当前位置: 首页 > 知识库问答 >
问题:

自动关闭的Java代理(Jedis资源)

王英彦
2023-03-14

我试图找出是否有可能创建Java动态代理来自动关闭自动关闭资源,而不必记住用try资源块嵌入这些资源。

例如,我有一个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)上调用method时,这个方法都会被传递给代理,代理从池中获取一个新的客户端,执行这个方法并将这个资源返回到池中。

它工作得很好,但当我想在客户机上执行多个方法时,每个方法的资源都会从池中取出并再次返回(这可能很耗时)。你知道如何改进吗?

共有2个答案

姚建树
2023-03-14

我不认为这是朝着正确的方向前进。毕竟,开发人员应该习惯于正确处理资源,并且IDE/编译器能够在未使用try(...){}处理自动关闭资源时发出警告...

然而,创建一个代理来装饰所有调用,并添加一种方法来装饰一批多个操作作为一个整体,这是一项一般性的任务,因此,它有一个通用的解决方案:

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);
        }
    }
}

请注意,池的类型转换

现在你可以像这样使用它了

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(…){}在实际的非代理资源上调用多个方法时,它们会让资源泄漏…

骆昊阳
2023-03-14

最终,您将拥有自己的“事务管理器”,在该管理器中,您通常会立即将对象返回到池中,但如果您启动了“事务”,则在“提交”该“事务”之前,对象不会返回到池中。

突然之间,由于使用了手工构建的自定义机制,使用try-with-Resources的问题变成了实际问题。

与资源专家一起使用try:

  • 语言内置功能
  • 允许你附加一个捕获块,资源仍然被释放
  • 简单、一致的语法,这样即使开发人员不熟悉它,他也会看到所有被它包围的Jedis代码,并(希望)认为“所以这一定是使用这个的正确方式”

欺骗:

  • 你需要记住使用它

你的建议优点(如果我忘记了什么,你可以告诉我):

  • 即使开发人员没有关闭资源,也会自动关闭,以防止资源泄漏

欺骗:

  • 额外的代码总是意味着额外的地方发现错误
  • 如果你不创建一个事务机制,你可能会遭受性能打击(我不熟悉[jr]edis或你的项目,所以我不能说这是否真的是一个问题)
  • 如果你真的创建了它,你会有更多的额外的代码,这很容易出错
  • 语法不再简单,对任何来项目的人来说都会感到困惑
  • 异常处理变得更加复杂
  • 您将通过反思进行所有代理调用(一个小问题,但嘿,这是我的列表;)
  • 可能更多,取决于最终的实现方式

如果你认为我没有提出有效的观点,请告诉我。否则,我的断言将仍然是“你有一个寻找问题的‘解决方案’”。

 类似资料:
  • 问题内容: 我正在编写一个连接到网站并从中读取一行的应用程序。我这样做是这样的: 好吗?我的意思是,我在最后一行关闭了BufferedReader,但没有关闭InputStreamReader。我是否应该从connection.getInputStream创建一个独立的InputStreamReader,并从独立的InputStreamReader创建一个BufferedReader,而不是关闭所

  • 本文向大家介绍Java使用 try-with-resources 实现自动关闭资源的方法,包括了Java使用 try-with-resources 实现自动关闭资源的方法的使用技巧和注意事项,需要的朋友参考一下 1、 在Java1.7之前,我们需要通过下面这种方法, 在finally中释放资源,这种方法有点繁琐。 2、在java1.7之后,可以使用try-with-resources实现自动关闭资

  • 在学习《 Java try catch finally语句》一节后我们可以发现,当程序使用 finally 块关闭资源时,程序会显得异常臃肿,例如以下代码。 Java 7 以前,上面程序中的 finally 代码块是不得不写的“臃肿代码”,为了解决这种问题,Java 7 增加了一个新特性,该特性提供了另外一种管理资源的方式,这种方式能自动关闭文件,被称为 自动资源管理(Automatic Reso

  • 我在windows 7中安装了android Studio,但它在打开后立即关闭。我还安装了java jdk并添加了一个JDK_HOME环境变量,但android Studio会在几秒钟后自行关闭。

  • 问题内容: 有人可以推荐几个不错的网站/ bloogs / RSS / Podcast,让对Java感兴趣的人可以随时了解最新趋势吗? (我在考虑像Ajaxian.com这样的Java语言) 一如既往,我们将永远感谢您的帮助。 问题答案: Java Posse播客:http : //www.javaposse.com/

  • 我是java新手。我正在尝试编写一个java代理。在使用演示应用程序运行时,它会失败,并显示以下消息: 下面是代码和pom文件:https://gist.github.com/proywm/f27b72d01aa66afc7f0f8c098e33e914 你能告诉我如何解决这个问题吗?