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 实例化。所以这就是我所做的:

  1. 在你的情况下添加一个抽象 class ThingImpl 来实现你的 Thing 接口,并将成为所有 ThnigAThingB ... classes 的父级。 (或者只需将您的 Thing 接口更改为抽象 Class)
  2. 将您的 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);
        }
    

    }

  3. 在您的抽象父级中添加以下构造函数

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");
    }
}