1

我有一个使用通过 Spring 注入的多个资源的Builder 。它看起来类似于:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class SandBoxBuilder {
    private final SandUtil sandUtil;
    private Sand sand;
    private Box box;

    @Autowired
    public SandBoxBuilder(SandUtil sandUtil) {
        this.sandUtil = sandUtil;
    }

    public SandBoxBuilder setSand(Sand sand) {
        this.sand = sand;
        return this;
    }

    public SandBoxBuilder setBox(Box box) {
        this.box = box;
        return this;
    }

    public SandBox build() {
        SandBox sandBox = new SandBox(sand);
        sandUtil.changeBox(sandBox, box);
        return sandBox;
    }
}

我遇到的问题是它不是线程安全的。我知道这个构建器不应该是一个单例,但我不确定如何使用弹簧注入的资源(SandUtil)而不连接构建器并将其注入我使用它的地方。

如何实现一个线程安全的构建器,该构建器利用 spring 注入的单例?

解决方案

由于一些架构限制,我无法将实用程序注入到我的调用类中。我最终实现了一个工厂构建器 bean,它返回一个具有对 spring 资源的引用的构建器的新实例。

解决方案实施

@Component
public class SandBoxBuilderFactory {
    private final SandUtil sandUtil;

    @Autowired
    public SandBoxBuilderFactory(SandUtil sandUtil) {
        this.sandUtil = sandUtil;
    }

    public Builder newBuilder(){
        return new Builder(sandUtil);
    }

    public static class Builder {
        private final SandUtil sandUtil;
        private Sand sand;
        private Box box;

        private Builder(SandUtil sandUtil) {
            this.sandUtil = sandUtil;
        }

        public Builder setSand(Sand sand) {
            this.sand = sand;
            return this;
        }

        public Builder setBox(Box box) {
            this.box = box;
            return this;
        }

        public SandBox build() {
            SandBox sandBox = new SandBox(sand);
            sandUtil.changeBox(sandBox, box);
            return sandBox;
        }

    }
}

用法

newBuilder().setBox(box).setSand(sand).build();
4

3 回答 3

2

SandBoxBuilder由于@Component. _ 无论您在哪里需要它,您都必须有权访问ApplicationContext. 我会建议,而不是注入SandBoxBuilderbean,注入SandUtilbean 并使用它来创建SandBoxBuilder实例

@Service
public class MyService {
    private final SandUtil sandUtil;

    @Autowired
    public MyService (SandUtil sandUtil) {
        this.sandUtil = sandUtil;
    }

    public void someMethod() {
        SandBoxBuilder builder = new SandBoxBuilder(sandUtil);
        ... // use it
    }
}

SandUtil必须是豆子吗?它可能适合作为static实用程序类。

于 2013-10-01T17:18:31.903 回答
0

最近对春季国际奥委会不太了解。我经常使用 Tapestry IOC,它应该提供类似的内部工作。

首先,根据定义,单例应该是线程安全的。因此,如果您每次使用它时都创建构建器,则构建器不需要是线程安全的。SandUtil 本身必须是线程安全的。

这就像一个合同:如果你是一个单例服务,你会被注入多个线程。因此,单例服务必须是线程安全的(同步方法、共享锁、同步对象等)。如果您的服务是 PerThread 意味着相同的服务仅在单个线程中使用,则它不必是线程安全的。

因此,确保 SandUtil 是线程安全的,如果 Sandbox 是 PerThread 或 PerOccurence(每次注入时都会创建新实例),那么您就可以了。

如果您想使构建器线程安全,因为您不能确定它的单个实例仅在线程中使用 - 而且您不太关心性能 - 您可以将同步关键字添加到构建器的每个非私有方法班级。这是穷人并发控制,否则请查看一些关于并发控制的教程,如原始 Java 课程

于 2013-10-01T17:08:06.430 回答
0

我猜这其中的非线程安全部分与 sandUtil 字段有关?

您可以对 changeBox 方法使用外部锁定来确保对其进行同步访问。

否则,也许“原型”bean 范围会帮助您?

http://docs.spring.io/spring/docs/3.0.x/reference/beans.html#beans-factory-scopes

http://docs.spring.io/spring/docs/3.0.x/reference/beans.html#beans-factory-scopes-prototype

于 2013-10-01T17:09:40.837 回答