当 Spring managed bean 和 Pojo 依赖相同的服务时如何避免 Singleton
How to avoid Singleton when Spring managed bean and Pojo depend on same service
我正在寻找单例模式的替代设计,前提条件如下:
- 有 is/are Spring 个使用某些实用程序服务的托管 bean
- 有 is/are 个使用相同实用程序服务的 POJO
- 服务的实施应该很容易mock/swap
有 JacksonMapper
实现了 JsonStringConverter
接口。
POJO 是由 Builder 模式创建的一些自定义对象,例如下面的示例,它使用接口 JsonStringConverter.
的实现
public class User {
private static final JsonStringConverter jsonStringConverter = JacksonMapper.getInstance();
private String name;
//...
private User (User builder) {
this.name = builder.name;
}
public String getName() {
return name;
}
public String toJson() {
Map<String, String> jsonMap = new LinkedHashMap<String, String>();
jsonMap.put(UserAtributes.NAME.getDescription(), this.name);
return jsonStringConverter.toJson(jsonMap);
}
public static class UserBuilder {
private String name;
//...
public UserBuilder name(String name) {
this.name = name;
return this;
}
public User build() {
return new User(this);
}
}
}
由于 User
的实例不受 Spring 管理,我无法使用 @Autowire
注释将 JsonStringConverter
的实现注入用户。
另一方面,有 Spring 托管 Bean 使用该接口的相同实现将一些其他映射转换为 Json 字符串。但是因为我已经有一个转换器实例缓存在 Spring IOC 之外,而不是在 IOC 内创建新对象只是为了注入,我还在 bean 中用 getInstance() 轮询它。这是我一点都不喜欢的部分。
这是有问题的单例:
public class JacksonMapper implements JsonStringConverter {
private static final ObjectMapper jsonToMap;
private static final ObjectMapper mapToJson;
private static final JacksonMapper INSTANCE;
static
{
INSTANCE = new JacksonMapper();
jsonToMap = new ObjectMapper();
mapToJson = new ObjectMapper();
}
/**
* Exists to defeat instantiation
*/
private JacksonMapper() {
// not called
}
public static JacksonMapper getInstance() {
return INSTANCE;
}
// methods...
}
我现在看到的选项:
- 让 JsonStringConverter 成为 class,使用静态方法从 Json 转换到 Json。 Spring 托管 bean 和 POJO 现在可以使用该实用程序 class 而不是单例。
缺点: 我更愿意在 JsonStringConverter
和客户端代码之间使用显式接口,以便模拟和轻松交换实现的可能性。
- 使用单例模式
缺点: 要求内存中有不必要的对象来调用本来应该是静态的方法。
有人可以为我指出上述情况的第三个合理选择吗?
请注意,当 Spring 托管 bean 和 POJO 都依赖于同一服务时,这是一般性问题,与此特定用例无关。
只需将您的单例注册为 Spring bean:
@Configuration
public class SingletonWrapperConfig {
@Bean
public JacksonMapper jacksonMapper() {
return JacksonMapper.getInstance();
}
}
请注意,在这种情况下,建议将 JacksonMapper
设为 enum
而不是普通的 class 作为 "enum singleton" 模式的一部分。
我正在寻找单例模式的替代设计,前提条件如下:
- 有 is/are Spring 个使用某些实用程序服务的托管 bean
- 有 is/are 个使用相同实用程序服务的 POJO
- 服务的实施应该很容易mock/swap
有 JacksonMapper
实现了 JsonStringConverter
接口。
POJO 是由 Builder 模式创建的一些自定义对象,例如下面的示例,它使用接口 JsonStringConverter.
的实现public class User {
private static final JsonStringConverter jsonStringConverter = JacksonMapper.getInstance();
private String name;
//...
private User (User builder) {
this.name = builder.name;
}
public String getName() {
return name;
}
public String toJson() {
Map<String, String> jsonMap = new LinkedHashMap<String, String>();
jsonMap.put(UserAtributes.NAME.getDescription(), this.name);
return jsonStringConverter.toJson(jsonMap);
}
public static class UserBuilder {
private String name;
//...
public UserBuilder name(String name) {
this.name = name;
return this;
}
public User build() {
return new User(this);
}
}
}
由于 User
的实例不受 Spring 管理,我无法使用 @Autowire
注释将 JsonStringConverter
的实现注入用户。
另一方面,有 Spring 托管 Bean 使用该接口的相同实现将一些其他映射转换为 Json 字符串。但是因为我已经有一个转换器实例缓存在 Spring IOC 之外,而不是在 IOC 内创建新对象只是为了注入,我还在 bean 中用 getInstance() 轮询它。这是我一点都不喜欢的部分。
这是有问题的单例:
public class JacksonMapper implements JsonStringConverter {
private static final ObjectMapper jsonToMap;
private static final ObjectMapper mapToJson;
private static final JacksonMapper INSTANCE;
static
{
INSTANCE = new JacksonMapper();
jsonToMap = new ObjectMapper();
mapToJson = new ObjectMapper();
}
/**
* Exists to defeat instantiation
*/
private JacksonMapper() {
// not called
}
public static JacksonMapper getInstance() {
return INSTANCE;
}
// methods...
}
我现在看到的选项:
- 让 JsonStringConverter 成为 class,使用静态方法从 Json 转换到 Json。 Spring 托管 bean 和 POJO 现在可以使用该实用程序 class 而不是单例。
缺点: 我更愿意在 JsonStringConverter
和客户端代码之间使用显式接口,以便模拟和轻松交换实现的可能性。
- 使用单例模式
缺点: 要求内存中有不必要的对象来调用本来应该是静态的方法。
有人可以为我指出上述情况的第三个合理选择吗?
请注意,当 Spring 托管 bean 和 POJO 都依赖于同一服务时,这是一般性问题,与此特定用例无关。
只需将您的单例注册为 Spring bean:
@Configuration
public class SingletonWrapperConfig {
@Bean
public JacksonMapper jacksonMapper() {
return JacksonMapper.getInstance();
}
}
请注意,在这种情况下,建议将 JacksonMapper
设为 enum
而不是普通的 class 作为 "enum singleton" 模式的一部分。