我有一个 Builder ,它使用通过Spring注入的多个资源。它看起来与此相似:
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();
答案 0 :(得分:2)
由于SandBoxBuilder
,您正在使用@Component
作为bean。无论您需要什么,您都必须能够访问ApplicationContext
。我建议,而不是注入SandBoxBuilder
bean,注入SandUtil
bean并使用它来创建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
需要是一个bean吗?它可能适合static
实用程序类。
答案 1 :(得分:0)
我最近对Spring IOC了解不多。我使用Tapestry IOC很多应该提供类似的内部工作。
首先,单个按照定义应该是线程安全的。因此,如果每次使用它时都创建构建器,则构建器不需要是线程安全的。 SandUtil本身必须是线程安全的。
这就像合同:如果你是单身人士服务,你会被注入多个线程。因此,单例服务必须是线程安全的(同步方法,共享锁,同步对象等)。如果您的服务是PerThread意味着相同的服务仅在单个线程中使用,则它不必是线程安全的。
因此,确保SandUtil是线程安全的,如果Sandbox是PerThread或PerOccurence(每次注入时都会创建新实例),就可以了。
如果你想使构建器线程安全,因为你不能确定它的单个实例只在一个线程中使用 - 并且你不关心性能 - 你可以只将synchronized关键字添加到每个非私有方法构建器类。这是穷人并发控制,否则请查看一些有关并发控制的教程,如original Java lesson
答案 2 :(得分: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