1

假设我们有一个如下所示的类。

public class MyClass {

   @ThreadScoped    
   private Collection<String> words = new ArrayList<String>();

   public void addWord(String word) {
        words.add(word);
   }

}

在运行时,我们只有这个类的一个实例,但它接受来自多个线程的调用。

我希望编写该类的开发人员能够忽略他们正在为多线程访问编写它的事实,而只是将该字段注释为“线程范围”。

是否可以创建一个方面,使每个调用线程都有自己的单词集合,就好像 words 字段本来是ThreadLocal? 即使在多次调用该方法时,集合也应该绑定到线程。

我已经看到了setget点切割,但无法找到一种使用它们的方法,它不需要我在对象上设置字段,这会破坏类的线程安全。

是否有另一种不使用方面的方法,例如使用代理或反射?

4

2 回答 2

2

您的设计中似乎有几个可能相互冲突的决定:

  • 您只有一个MyClass.

  • 您希望每个线程查看其自己的words字段版本。

在我看来,为此目的使用 AspectJ 可能是矫枉过正。我想线程特定的访问可以被认为是一个横切关注点,但我不确定 AOP 是处理这个问题的方法。在尝试 AspectJ 之前,我会首先考虑重新设计纯 Java 代码。

老实说,我相信实现第二种效果的最简单方法是让每个线程都有自己的MyClass. 如果不能明确地将其传递给每个线程,您甚至可以使用 aThreadLocal来提供对正确实例的访问。MyClass

如果您的字段MyClass由多个线程共享,那么您可能需要考虑重构MyClass以将其分成两个类。这会将特定于线程的字段与共享字段分开,从而允许您以不同的方式处理每种情况。

编辑:

我稍微考虑了您的评论,在我看来,您正在尝试使用 AOP 从本质上向开发人员隐藏该字段的实现。将编程接口与实际实现分离的问题主要在面向对象编程中得到解决,而没有解决到 AOP。

在您的情况下,要走的路是使用抽象超类MyClass来为该字段保存一个私有声明,然后使用几个 getter 和 setter 方法来MyClass处理它。这样你就可以改变实际的实现而不影响其他任何东西。

public abstract AbstractMyClass {
    private ... words = ...;

    protected Collection<String> getWords() {
        ...
    }
}

public Myclass extends AbstractMyClass {
   public void addWord(String word) {
        this.getWords().add(word);
   }
}
于 2013-02-27T17:40:49.913 回答
1

由于以下几个原因,您想要以通用方式使用 AspectJ 是不可能的:

  • 没有通用的方法可以在创建时克隆对象,即使是集合也不行。然后考虑原始类型。
  • 即使您确实将对象克隆到ThreadLocal特定set()切入点中的 a 中,您如何将对象上的方法调用重定向到克隆?

话虽如此,一种方法是编写一个源代码预处理器来做一些魔术(包装有问题的对象和对它们的方法调用)。预处理器会将其代码编织到源代码中,然后您通常会使用javac编译它们。

无论如何,正如 thkala 所说,我认为最简单的解决方案是教您的开发人员自己实际使用ThreadLocal或使用您的 API 提供的本地化包装器(您必须实现)。

于 2013-03-16T13:31:47.217 回答