如何在运行时拦截对象的所有读写访问?

时间:2011-01-19 10:51:26

标签: java multithreading

我正在研究一个老化的Java“Swing”应用程序,不幸的是,它使用非线程安全的数据模型。随着具有多线程和多核PC的客户端数量的增加,由于非同步访问数据而导致的伪随机错误数量也在增加。我们有一个巨大的代码库,类似于6000个Java文件,其中大部分都非常庞大和复杂。

我想创建一个全局静态ReentrantReadWriteLock来控制对模型的访问。我的计划是创建一个虚拟的ReadWriteLock实现,它总是成功但不做任何事情,并在生产系统上使用它,同时在测试系统上使用ReentrantReadWriteLock,以便转换可以是渐进的。

我需要的是一种“拦截”测试系统中对模型的所有访问的方法,如果访问不在适当的锁定/解锁块的范围内,则记录错误。 ReentrantReadWriteLock可以告诉您是否拥有写锁定。我假设我可以通过使用ThreadLocal或其他东西来跟踪读锁定状态。

现在出现了一个真正的问题:在Java中,我怎么能编写代码,在运行时拦截对定义良好的类集中​​的任何公共方法的任何访问,并检查访问是否在锁定/解锁内阻止,通过查询全局锁?可以用AspectJ完成吗?还是其他什么?怎么样?

3 个答案:

答案 0 :(得分:1)

您可能会发现java proxies有用 您不能将invokation处理程序添加到现有对象,但您可以在代理中“包装”您的类的实例。创建的代理对象仍然是MyClass类型。

答案 1 :(得分:1)

我认为它可以用AspectJ完成但是我没有用它来知道如何。

相反,我要做的是使用要捕获的类的修改副本并将它们添加到bootclasspath。例如更改FileInputStream和FileOutputStream的副本。

对于测试系统,这项工作非常简单。我不会鼓励你在制作中这样做。

答案 2 :(得分:1)

我不知道这对您的情况有多适用,但拦截方法调用的最佳方法是使用动态代理。为此,您必须为您的类创建接口,无论如何您都可以这样做。

以下是动态代理如何工作的示例:

 interface Foo {
    void setBar( String bar );
    String getBar();
 }

 class FooImpl implements Foo {
    private String bar;
    // getter and setter
 }

这是您的正常代码。现在你可以做的是代替new FooImpl()你可以使用代理。对于代理,您首先需要[InvocationHandler][1]

 class LockHandler implements InvocationHandler {

    private FooImpl originalOb;

    public LockHandler( FooImpl originalOb ) {
      this.originalOb = originalOb;
    }

    @Override
    public Object invoke( Object proxy, Method method, Object [] args ) {
      try {
        if( method.getName().startsWith( "set" ) {
           // do locking stuff
        }
        return method.invoke( originalOb, args ); //invoke underlying method
      } finally {
        if( method.getName().startsWith( "set" ) {
           // do post-call locking stuff
        }
      }     
    }
 }

然后你最终创建了你的Proxy对象:

 Foo foo = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
                                      new Class[] { Foo.class },
                                      new LockHandler( new FooImpl() ) );

现在,对foo对象的每次调用都将被引导到您的调用处理程序中。当然,您不必为每个对象创建一个新的调用处理程序实例,您甚至不需要为每个接口单独使用一个,originalOb可以(并且在现实生活中)应该声明为{{1 }类型。

另一种选择是使用AspectJ这样的方面实现,它可能更易于使用,但引入它可能为时已晚,具体取决于您在软件开发方面的进展。