1

我想装饰接口PreparedStatement,以便自定义关闭它(只是一个例子)。

这意味着我想装饰 的现有实例PreparedStatement,从而在调用其他代码时close()调用它。

为此,我需要默认实现装饰器的所有数十种方法,PreparedStatement只是为了将调用委托给内部对象,就像在此处完成的那样。缺点是它只是大量的工作和代码,几乎没有附加价值。

另一种选择是尝试使用 Java 的Proxy 和 InvocationHandler以提供默认实现,该实现在单个方法中为所有方法执行委托。如果存在自定义方法,则InvocationHandler会将调用定向到该方法。请参见此处的示例。此解决方案的问题是无法将自定义方法标记为@Override并且无法检查其签名的正确性,因为它需要一个 abstract PreparedStatement,代理将无法实例化它。

那么,这可以做到吗?如何?

* 必须能够使用 Java 7 max 来实现,但请随时提供 Java 8 答案。

4

3 回答 3

0

您能否更清楚地解释Proxy解决方案缺少什么?考虑这样的事情,它依赖于 AOP 式的“钩子”:

final PreparedStatement original = ...;
final InvocationHandler delegator = new InvocationHandler() {

  void onClose() {
    /* do stuff */
  }

  Object invoke(final Object proxy, final Method method, final Object[] args) {
    if (method.getName().equals("close")) {
      onClose();
    }

    return method.invoke(original, args);
  }
};
final PreparedStatement wrapped = (PreparedStatement) Proxy.newProxyInstance(this.getClass().getClassLoader(),
    new Class<?>[] { PreparedStatement.class }, delegator);
于 2016-06-28T16:21:45.053 回答
0

据我了解,您希望为接口 PreparedStatement 提供具体实现。我能想到的唯一方法是创建实现接口的抽象类。通过这样做,您不需要实现接口中的所有方法,并且您将获得所需的实现。

我会尝试这样的事情:

public abstract class MyPreparedStatement implements PreparedStatement {

@Override
public void close() throws SQLException {
    System.out.println("Closing");
}

public static void main(String[] args) throws SQLException {
    Connection con = null;
    MyPreparedStatement statement = (MyPreparedStatement) con.prepareStatement("sql");
}
}
于 2016-06-27T13:26:12.657 回答
-1

如果您无权访问这些方法以便对它们进行通常的继承操作,您可以使用AspectJ或 Spring Framework 方面功能来完成您尝试使用面向方面编程所做的事情,以提供有关您所需方法的建议.

一个简单的方面基本上归结为:

@Aspect
public class MyAspect {

    @Pointcut("execution(* *(..))") //Replace expression with target method; this example 
    //will hit literally every method ever.
    public void targetmethod() {}; //Intentionally blank.
    //AspectJ uses byte code manipulation (or "black magic voodoo", if you 
    // will) to make this method a surrogate for any real one that matches the pointcut

    @Before("targetmethod()") //Or @After, or @Around, etc...
    public void doStuff() throws Throwable {
        //Put your code here
    }
}

一旦你有了你的方面,将它们添加到你的 aop.xml 并编织你的方面(你可以在编译时使用适当的构建管理器配置来执行此操作,或者在运行时通过运行 aspectjweaver 来执行此操作java -javaagent:/path/to/aspectjweaver.jar)。

然而,这确实带有免责声明:对 java.* 类做这样的事情可以让你以新的和有趣的方式破坏你正在引入的所有副作用(事实上,AspectJWeaver 拒绝编织到 java.* 类中默认值,尽管您可以覆盖该设置)。非常清楚你在做什么,并明智地使用你的方面和方面的方法。

于 2016-06-27T13:53:19.863 回答