Spring DI,工厂服务
Spring DI, factory services
这与其说是代码实现,不如说是设计问题,但我希望有人能帮忙。
我面临的问题是我需要根据两个输入注入一个 modelSerivce。基于一些文档和 SO 问题,我很确定我想为此使用 factorybean。
我的问题更多地涉及将使用 factorybean 创建的这些 classes 的构造。我怎样才能在工厂中重复使用单例 bean,而不是每次调用工厂时都创建一个新的 class?
代码如下所示:
事物接口:
public interface Thing {
void Run();
}
ThingA 实现:
public class ThingA implements Thing{
public void Run() {
System.out.println("In ThingA");
}
}
ThingB 实施:
public class ThingB implements Thing{
public void Run() {
System.out.println("In ThingB");
}
}
ThingFactory 实施:
public class ThingFactory {
public Thing GetThing(String stateCode, Date date){
Thing result;
if(stateCode == "MA") {
result = new ThingA();
}
else {
result = new ThingB();
}
return result;
}
}
我真正希望工厂做的是提取一个已知的实现,而不是每次调用工厂时都创建一个实现。我也不想通过这样做将我的工厂绑定到 Spring 框架:
ApplicationContext.getBean()
只需像往常一样将 bean 保持在 spring 上下文中作为单例。将上下文注入您的工厂并根据您的业务规则从中提取 bean。
public class ThingFactory {
@Autowired
private ApplicationContext ctx;
public Thing GetThing(String stateCode, Date date){
Thing result;
if(stateCode == "MA") {
result = ctx.getBean("someBean")
}
else {
result = ctx.getBean("someOtherBean")
}
return result;
}
}
你可以更聪明,使用方案来命名上下文中的 bean:
@Service("Thing_MA")
public class ThingA implements Thing{
.
.
.
}
这为您的工厂购买了很好的声明式查找规则:
public class ThingFactory {
public Thing GetThing(String stateCode, Date date){
return (Thing) ctx.getBean("Thing_" + stateCode);
}
}
实际上我处理过同样的问题,这是我想出的解决方案。我依赖的主要技巧是任何定义为单例的 bean 在 Spring 初始化时由 Spring 实例化。所以这就是我所做的:
- 在你的情况下添加一个抽象 class
ThingImpl
来实现你的 Thing
接口,并将成为所有 ThnigA
、ThingB
... classes 的父级。 (或者只需将您的 Thing
接口更改为抽象 Class)
将您的 ThingFactory
更改为如下内容:
public class ThingFactory {
private static Map<String, Thing> instancesMap = new Hashmap<>()
public static Thing getThing(String name) {
return instancesMap.get(name);
}
public static addInstance(String name, Thing thing) {
instancesMap.put(name, thing);
}
}
在您的抽象父级中添加以下构造函数
public ThingImpl() {
ThingFactory.addInstance(this.getClass().getSimpleName(), this);
}
你的工厂根本不需要被定义为bean,但是你所有的Thing
classes 都应该被定义为bean。将会发生的是,当 Spring 初始化时,它将实例化所有 Thing
classes 并且作为其自身初始化的一部分,每个 Thing
class 将自己插入到你工厂里的一张地图。所以你现在所要做的就是在你的代码中的任何地方调用你的工厂:
ThingFactory.getThing("ThingA");
ThingFactory.getThing("ThingB");
如果您不想绑定代码 spring 框架,这意味着您不想 BeanFactory
并创建自己的工厂,那么您将不得不自己创建对象,例如
public class ThingFactory {
private final static Thing thingA = new ThingA();
private final static Thing thingB = new ThingB();
public Thing GetThing(String stateCode, Date date){
if(stateCode.equals("MA")) {
return thingA;
} else {
return thingB;
}
}
}
如果您有更多 Thing
的实现,在这种情况下,您只需创建一个地图并根据需要获取对象。
public class ThingFactory {
private final static Map<String, Thing> beanMap = new Hashmap<>()
public ThingFactory(){
addThing("ThingA", new ThingA());
addThing("ThingB", new ThingB());
}
public static Thing getThing(String name) {
return beanMap.get(name);
}
public static addThing(String name, Thing thing) {
beanMap.put(name, thing);
}
public Thing GetThing(String stateCode, Date date){
if(stateCode.equals("MA")) {
return getThing("ThingA");
} else {
return getThing("ThingB");
}
}
}
但是,我建议您使用 Spring 的 BeanFactory
而不是创建自己的工厂并使用 @Robert Moskal 的答案。
如果 stateCode
的值在应用程序启动时已知,您可以使用 spring profiles
来实现。
@Service
@Profile("ThingB")
public class ThingB implements Thing{
public void Run() {
System.out.println("In ThingB");
}
}
这与其说是代码实现,不如说是设计问题,但我希望有人能帮忙。
我面临的问题是我需要根据两个输入注入一个 modelSerivce。基于一些文档和 SO 问题,我很确定我想为此使用 factorybean。
我的问题更多地涉及将使用 factorybean 创建的这些 classes 的构造。我怎样才能在工厂中重复使用单例 bean,而不是每次调用工厂时都创建一个新的 class?
代码如下所示:
事物接口:
public interface Thing {
void Run();
}
ThingA 实现:
public class ThingA implements Thing{
public void Run() {
System.out.println("In ThingA");
}
}
ThingB 实施:
public class ThingB implements Thing{
public void Run() {
System.out.println("In ThingB");
}
}
ThingFactory 实施:
public class ThingFactory {
public Thing GetThing(String stateCode, Date date){
Thing result;
if(stateCode == "MA") {
result = new ThingA();
}
else {
result = new ThingB();
}
return result;
}
}
我真正希望工厂做的是提取一个已知的实现,而不是每次调用工厂时都创建一个实现。我也不想通过这样做将我的工厂绑定到 Spring 框架:
ApplicationContext.getBean()
只需像往常一样将 bean 保持在 spring 上下文中作为单例。将上下文注入您的工厂并根据您的业务规则从中提取 bean。
public class ThingFactory {
@Autowired
private ApplicationContext ctx;
public Thing GetThing(String stateCode, Date date){
Thing result;
if(stateCode == "MA") {
result = ctx.getBean("someBean")
}
else {
result = ctx.getBean("someOtherBean")
}
return result;
}
}
你可以更聪明,使用方案来命名上下文中的 bean:
@Service("Thing_MA")
public class ThingA implements Thing{
.
.
.
}
这为您的工厂购买了很好的声明式查找规则:
public class ThingFactory {
public Thing GetThing(String stateCode, Date date){
return (Thing) ctx.getBean("Thing_" + stateCode);
}
}
实际上我处理过同样的问题,这是我想出的解决方案。我依赖的主要技巧是任何定义为单例的 bean 在 Spring 初始化时由 Spring 实例化。所以这就是我所做的:
- 在你的情况下添加一个抽象 class
ThingImpl
来实现你的Thing
接口,并将成为所有ThnigA
、ThingB
... classes 的父级。 (或者只需将您的Thing
接口更改为抽象 Class) 将您的
ThingFactory
更改为如下内容:public class ThingFactory { private static Map<String, Thing> instancesMap = new Hashmap<>() public static Thing getThing(String name) { return instancesMap.get(name); } public static addInstance(String name, Thing thing) { instancesMap.put(name, thing); }
}
在您的抽象父级中添加以下构造函数
public ThingImpl() {
ThingFactory.addInstance(this.getClass().getSimpleName(), this);
}
你的工厂根本不需要被定义为bean,但是你所有的Thing
classes 都应该被定义为bean。将会发生的是,当 Spring 初始化时,它将实例化所有 Thing
classes 并且作为其自身初始化的一部分,每个 Thing
class 将自己插入到你工厂里的一张地图。所以你现在所要做的就是在你的代码中的任何地方调用你的工厂:
ThingFactory.getThing("ThingA");
ThingFactory.getThing("ThingB");
如果您不想绑定代码 spring 框架,这意味着您不想 BeanFactory
并创建自己的工厂,那么您将不得不自己创建对象,例如
public class ThingFactory {
private final static Thing thingA = new ThingA();
private final static Thing thingB = new ThingB();
public Thing GetThing(String stateCode, Date date){
if(stateCode.equals("MA")) {
return thingA;
} else {
return thingB;
}
}
}
如果您有更多 Thing
的实现,在这种情况下,您只需创建一个地图并根据需要获取对象。
public class ThingFactory {
private final static Map<String, Thing> beanMap = new Hashmap<>()
public ThingFactory(){
addThing("ThingA", new ThingA());
addThing("ThingB", new ThingB());
}
public static Thing getThing(String name) {
return beanMap.get(name);
}
public static addThing(String name, Thing thing) {
beanMap.put(name, thing);
}
public Thing GetThing(String stateCode, Date date){
if(stateCode.equals("MA")) {
return getThing("ThingA");
} else {
return getThing("ThingB");
}
}
}
但是,我建议您使用 Spring 的 BeanFactory
而不是创建自己的工厂并使用 @Robert Moskal 的答案。
如果 stateCode
的值在应用程序启动时已知,您可以使用 spring profiles
来实现。
@Service
@Profile("ThingB")
public class ThingB implements Thing{
public void Run() {
System.out.println("In ThingB");
}
}