春天DI,工厂服务

时间:2017-08-16 02:13:10

标签: java spring factory-pattern

这更像是一个设计问题,而不是代码实现,但我希望有人可以提供帮助。

我面临的问题是我需要根据两个输入注入一个modelSerivce。我非常肯定基于一些文档和SO问题,我想为此使用一个factorybean。

我的问题更多地涉及将使用factorybean创建的这些类的构造。如何在工厂中重复使用单件bean而不是每次调用工厂时创建一个新类?

代码如下所示:

事物接口:

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()

3 个答案:

答案 0 :(得分:2)

实际上我处理了同样的问题,这是我提出的解决方案。我所依赖的主要技巧是,任何定义为单线程的bean都是在Spring初始化时由Spring实现的。所以这就是我所做的:

  1. 在您的情况下,添加一个实现ThingImpl的抽象类Thing 界面,将成为您所有ThnigAThingB ...类的父级。 (或者只是将您的Thing界面更改为抽象类)
  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. 在您的抽象父级中添加以下构造函数

  4. public ThingImpl() { ThingFactory.addInstance(this.getClass().getSimpleName(), this); }

    您的工厂根本不需要定义为bean,但所有Thing类都应定义为bean。将要发生的事情是,当Spring初始化时,它将实例化所有Thing类,并且作为其自身初始化的一部分,每个Thing类将自己插入到工厂的地图中。因此,您现在需要做的就是在您的代码中的任何地方,您可以致电您的工厂:

    ThingFactory.getThing("ThingA");
    ThingFactory.getThing("ThingB");
    

答案 1 :(得分:1)

只需像往常一样将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);

   }
}

答案 2 :(得分:0)

如果您不想绑定代码弹簧框架,则意味着您不想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");
    }
}