使用Spring 3注释实现一个简单的工厂模式

blo*_*824 43 java spring annotations factory-pattern

我想知道如何用Spring 3注释实现简单的工厂模式.我在文档中看到,您可以创建调用工厂类并运行工厂方法的bean.我想知道这是否可能只使用注释.

我有一个目前正在呼叫的控制器

MyService myService = myServiceFactory.getMyService(test);
result = myService.checkStatus();
Run Code Online (Sandbox Code Playgroud)

MyService是一个名为checkStatus()的方法的接口.

我的工厂类看起来像这样:

@Component
public class MyServiceFactory {

    public static MyService getMyService(String service) {
        MyService myService;

        service = service.toLowerCase();

        if (service.equals("one")) {
            myService = new MyServiceOne();
        } else if (service.equals("two")) {
            myService = new MyServiceTwo();
        } else if (service.equals("three")) {
            myService = new MyServiceThree();
        } else {
            myService = new MyServiceDefault();
        }

        return myService;
    }
}
Run Code Online (Sandbox Code Playgroud)

MyServiceOne类如下所示:

@Autowired
private LocationService locationService;

public boolean checkStatus() {
      //do stuff
}
Run Code Online (Sandbox Code Playgroud)

当我运行此代码时,locationService变量为alwasy null.我相信这是因为我在工厂里自己创造了这些物品并且没有发生自动装配.有没有办法添加注释才能使其正常工作?

谢谢

Dru*_*uma 67

以下对我有用:

接口由您的逻辑方法和附加的身份方法组成:

public interface MyService {
    String getType();
    void checkStatus();
}
Run Code Online (Sandbox Code Playgroud)

一些实现:

@Component
public class MyServiceOne implements MyService {
    @Override
    public String getType() {
        return "one";
    }

    @Override
    public void checkStatus() {
      // Your code
    }
}

@Component
public class MyServiceTwo implements MyService {
    @Override
    public String getType() {
        return "two";
    }

    @Override
    public void checkStatus() {
      // Your code
    }
}

@Component
public class MyServiceThree implements MyService {
    @Override
    public String getType() {
        return "three";
    }

    @Override
    public void checkStatus() {
      // Your code
    }
}
Run Code Online (Sandbox Code Playgroud)

工厂本身如下:

@Service
public class MyServiceFactory {

    @Autowired
    private List<MyService> services;

    private static final Map<String, MyService> myServiceCache = new HashMap<>();

    @PostConstruct
    public void initMyServiceCache() {
        for(MyService service : services) {
            myServiceCache.put(service.getType(), service);
        }
    }

    public static MyService getService(String type) {
        MyService service = myServiceCache.get(type);
        if(service == null) throw new RuntimeException("Unknown service type: " + type);
        return service;
    }
}
Run Code Online (Sandbox Code Playgroud)

我发现这样的实现更容易,更清晰,更具可扩展性.添加新的MyService就像创建另一个实现相同接口的spring bean一样简单,而不会在其他地方进行任何更改.

  • “private List&lt;MyService&gt; services ”是从哪里初始化的。它需要由 Spring 创建和管理的 bean,而不是手动创建的 bean。 (2认同)

Tom*_*icz 34

你是对的,通过手动创建对象,你不会让Spring执行自动装配.考虑通过Spring管理您的服务:

@Component
public class MyServiceFactory {

    @Autowired
    private MyServiceOne myServiceOne;

    @Autowired
    private MyServiceTwo myServiceTwo;

    @Autowired
    private MyServiceThree myServiceThree;

    @Autowired
    private MyServiceDefault myServiceDefault;

    public static MyService getMyService(String service) {
        service = service.toLowerCase();

        if (service.equals("one")) {
            return myServiceOne;
        } else if (service.equals("two")) {
            return myServiceTwo;
        } else if (service.equals("three")) {
            return myServiceThree;
        } else {
            return myServiceDefault;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

但我认为总体设计相当差.拥有一个通用MyService实现并将one/ two/ threestring作为额外参数传递给不是更好checkStatus()吗?你想达到什么目的?

@Component
public class MyServiceAdapter implements MyService {

    @Autowired
    private MyServiceOne myServiceOne;

    @Autowired
    private MyServiceTwo myServiceTwo;

    @Autowired
    private MyServiceThree myServiceThree;

    @Autowired
    private MyServiceDefault myServiceDefault;

    public boolean checkStatus(String service) {
        service = service.toLowerCase();

        if (service.equals("one")) {
            return myServiceOne.checkStatus();
        } else if (service.equals("two")) {
            return myServiceTwo.checkStatus();
        } else if (service.equals("three")) {
            return myServiceThree.checkStatus();
        } else {
            return myServiceDefault.checkStatus();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

仍然设计得很差,因为添加新的MyService实现也需要MyServiceAdapter修改(SRP违规).但这实际上是一个很好的起点(提示:地图和战略模式).

  • 第一个选项不起作用,因为getMyService()是静态的.因此myServiceOne,myServiceTwo也必须是静态的. (7认同)
  • 好问题。也许是因为我们想将客户端与不同的实现解耦?看看我同时所做的编辑。 (3认同)

小智 26

以下DruidKuma 的回答

用自动装配的构造函数重构他的工厂:

@Service
public class MyServiceFactory {

    private static final Map<String, MyService> myServiceCache = new HashMap<>();

    @Autowired
    private MyServiceFactory(List<MyService> services) {
        for(MyService service : services) {
            myServiceCache.put(service.getType(), service);
        }
    }

    public static MyService getService(String type) {
        MyService service = myServiceCache.get(type);
        if(service == null) throw new RuntimeException("Unknown service type: " + type);
        return service;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 尼斯帕维尔,为了锦上添花,请考虑将构造函数的可访问性更改为私有,因为该类不需要从外部构造/访问,并且 Spring 仍然能够通过反射进行自动装配。我测试了它并且它有效。 (2认同)

Jef*_*ang 9

为什么不将接口FactoryBean添加到MyServiceFactory(告诉Spring它是一个工厂),添加一个寄存器(String service,MyService instance)然后,让每个服务调用:

@Autowired
MyServiceFactory serviceFactory;

@PostConstruct
public void postConstruct() {
    serviceFactory.register(myName, this);
}
Run Code Online (Sandbox Code Playgroud)

这样,您可以根据需要将每个服务提供商分成模块,Spring将自动选择任何已部署和可用的服务提供商.

  • 只是一个警告,我在使用 @Transactional 注释注释的方法为 spring 服务类实现类似代码时遇到了一个问题。基本上,当您使用 `this` 关键字注册一个类,并且该类被代理时,传递给您的服务工厂的类不会被代理。见 https://spring.io/blog/2012/05/23/transactions-caching-and-aop-understanding-proxy-usage-in-spring (2认同)

rog*_*zes 8

您可以手动要求Spring自动装配它.

让您的工厂实现ApplicationContextAware.然后在您的工厂中提供以下实现:

@Override
public void setApplicationContext(final ApplicationContext applicationContext) {
    this.applicationContext = applicationContext;
}
Run Code Online (Sandbox Code Playgroud)

然后在创建bean之后执行以下操作:

YourBean bean = new YourBean();
applicationContext.getAutowireCapableBeanFactory().autowireBean(bean);
bean.init(); //If it has an init() method.
Run Code Online (Sandbox Code Playgroud)

这将自动装配您的LocationService完全正常.


kyi*_*yiu 6

您还可以声明性地定义一个ServiceLocatorFactoryBean类型的bean ,它将充当Factory类.它得到了Spring 3的支持.

一个FactoryBean实现,它接受一个必须有一个或多个带签名方法的接口(通常是MyService getService()或MyService getService(String id))并创建一个实现该接口的动态代理

这是使用Spring实现Factory模式的示例

一个更明显的例子


And*_*ver 5

基于 Pavel ?erný这里的解决方案, 我们可以对该模式进行通用类型化实现。为此,我们需要引入 NamedService 接口:

    public interface NamedService {
       String name();
    }
Run Code Online (Sandbox Code Playgroud)

并添加抽象类:

public abstract class AbstractFactory<T extends NamedService> {

    private final Map<String, T> map;

    protected AbstractFactory(List<T> list) {
        this.map = list
                .stream()
                .collect(Collectors.toMap(NamedService::name, Function.identity()));
    }

    /**
     * Factory method for getting an appropriate implementation of a service
     * @param name name of service impl.
     * @return concrete service impl.

     */
    public T getInstance(@NonNull final String name) {
        T t = map.get(name);
        if(t == null)
            throw new RuntimeException("Unknown service name: " + name);
        return t;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后我们创建一个像 MyService 这样的特定对象的具体工厂:

 public interface MyService extends NamedService {
           String name();
           void doJob();
 }

@Component
public class MyServiceFactory extends AbstractFactory<MyService> {

    @Autowired
    protected MyServiceFactory(List<MyService> list) {
        super(list);
    }
}
Run Code Online (Sandbox Code Playgroud)

其中列出编译时 MyService 接口的实现列表。

如果您跨应用程序有多个类似的工厂按名称生成对象(如果按名称生成对象当然满足您的业务逻辑),则此方法工作正常。这里的 map 可以很好地将 String 作为键,并保存您服务的所有现有实现。

如果你有不同的生成对象的逻辑,这个额外的逻辑可以移到另一个地方,并与这些工厂(按名称获取对象)结合使用。