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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6390810/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-16 05:33:05  来源:igfitidea点击:

Implement a simple factory pattern with Spring 3 annotations

javaspringannotationsfactory-pattern

提问by blong824

I was wondering how I could implement the simple factory pattern with Spring 3 annotations. I saw in the documentation that you can create beans that call the factory class and run a factory method. I was wondering if this was possible using annotations only.

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

I have a controller that currently calls

我有一个当前调用的控制器

MyService myService = myServiceFactory.getMyService(test);
result = myService.checkStatus();

MyService is an interface with one method called checkStatus().

MyService 是一个接口,其中包含一个名为 checkStatus() 的方法。

My factory class looks like this:

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

@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;
    }
}

MyServiceOne class looks like this :

MyServiceOne 类如下所示:

@Autowired
private LocationService locationService;

public boolean checkStatus() {
      //do stuff
}

When I run this code the locationService variable is alwasy null. I beleive this is because I am creating the objects myself inside the factory and autowiring is not taking place. Is there a way to add annotations to make this work correctly?

当我运行此代码时,locationService 变量始终为空。我相信这是因为我自己在工厂内部创建对象并且没有进行自动装配。有没有办法添加注释以使其正常工作?

Thanks

谢谢

采纳答案by Tomasz Nurkiewicz

You are right, by creating object manually you are not letting Spring to perform autowiring. Consider managing your services by Spring as well:

您是对的,通过手动创建对象,您不会让 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;
        }
    }
}

But I would consider the overall design to be rather poor. Wouldn't it better to have one general MyServiceimplementation and pass one/two/threestring as extra parameter to checkStatus()? What do you want to achieve?

但我认为整体设计相当糟糕。那岂不是更好有一个一般的MyService执行和传递one/ two/three字符串作为额外的参数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();
        }
    }
}

This is stillpoorly designed because adding new MyServiceimplementation requires MyServiceAdaptermodification as well (SRP violation). But this is actually a good starting point (hint: map and Strategy pattern).

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

回答by Jeff Wang

Why not add the interface FactoryBean to MyServiceFactory (to tell Spring that it's a factory), add a register(String service, MyService instance) then, have each of the services call:

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

@Autowired
MyServiceFactory serviceFactory;

@PostConstruct
public void postConstruct() {
    serviceFactory.register(myName, this);
}

This way, you can separate each service provider into modules if necessary, and Spring will automagically pick up any deployed and available service providers.

这样,您可以在必要时将每个服务提供者分成模块,Spring 将自动选择任何已部署和可用的服务提供者。

回答by rogermenezes

You can manually ask Spring to Autowire it.

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

Have your factory implement ApplicationContextAware. Then provide the following implementation in your factory:

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

@Override
public void setApplicationContext(final ApplicationContext applicationContext) {
    this.applicationContext = applicationContext;
}

and then do the following after creating your bean:

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

YourBean bean = new YourBean();
applicationContext.getAutowireCapableBeanFactory().autowireBean(bean);
bean.init(); //If it has an init() method.

This will autowire your LocationService perfectly fine.

这将自动装配您的 LocationService 非常好。

回答by kyiu

You could also declaratively define a bean of type ServiceLocatorFactoryBeanthat will act as a Factory class. it supported by Spring 3.

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

A FactoryBean implementation that takes an interface which must have one or more methods with the signatures (typically, MyService getService() or MyService getService(String id)) and creates a dynamic proxy which implements that interface

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

Here's an example of implementing the Factory pattern using Spring

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

One more clearly example

一个更清楚的例子

回答by Mikhail

I suppose you to use org.springframework.beans.factory.config.ServiceLocatorFactoryBean. It will much simplify your code. Except MyServiceAdapter u can only create interface MyServiceAdapter with method MyService getMyService and with alies to register your classes

我想你使用 org.springframework.beans.factory.config.ServiceLocatorFactoryBean。它将大大简化您的代码。除了 MyServiceAdapter,您只能使用 MyService getMyService 方法和别名创建接口 MyServiceAdapter 来注册您的类

Code

代码

bean id="printStrategyFactory" class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean">
        <property name="YourInterface" value="factory.MyServiceAdapter" />
    </bean>

    <alias name="myServiceOne" alias="one" />
    <alias name="myServiceTwo" alias="two" />

回答by OlivierTerrien

You could instantiate "AnnotationConfigApplicationContext" by passing all your service classes as parameters.

您可以通过将所有服务类作为参数传递来实例化“AnnotationConfigApplicationContext”。

@Component
public class MyServiceFactory {

    private ApplicationContext applicationContext;

    public MyServiceFactory() {
        applicationContext = new AnnotationConfigApplicationContext(
                MyServiceOne.class,
                MyServiceTwo.class,
                MyServiceThree.class,
                MyServiceDefault.class,
                LocationService.class 
        );
        /* I have added LocationService.class because this component is also autowired */
    }

    public MyService getMyService(String service) {

        if ("one".equalsIgnoreCase(service)) {
            return applicationContext.getBean(MyServiceOne.class);
        } 

        if ("two".equalsIgnoreCase(service)) {
            return applicationContext.getBean(MyServiceTwo.class);
        } 

        if ("three".equalsIgnoreCase(service)) {
            return applicationContext.getBean(MyServiceThree.class);
        } 

        return applicationContext.getBean(MyServiceDefault.class);
    }
}

回答by DruidKuma

The following worked for me:

以下对我有用:

The interface consist of you logic methods plus additional identity method:

该接口由您的逻辑方法和附加标识方法组成:

public interface MyService {
    String getType();
    void checkStatus();
}

Some implementations:

一些实现:

@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
    }
}

And the factory itself as following:

工厂本身如下:

@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;
    }
}

I've found such implementation easier, cleaner and much more extensible. Adding new MyService is as easy as creating another spring bean implementing same interface without making any changes in other places.

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

回答by Pavel ?erny

Following answer of DruidKuma

以下DruidKuma 的回答

Litte refactor of his factory with autowired constructor:

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

@Service
public class MyServiceFactory {

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

    @Autowired
    public 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;
    }
}

回答by Viren Dholakia

Try this:

尝试这个:

public interface MyService {
 //Code
}

@Component("One")
public class MyServiceOne implements MyService {
 //Code
}

@Component("Two")
public class MyServiceTwo implements MyService {
 //Code
}

回答by Andreas Gelever

Based on solution by Pavel ?erny herewe can make an universal typed implementation of this pattern. To to it, we need to introduce NamedService interface:

帕维尔通过基于解决方案吗?erny这里我们可以做一个通用的类型实现这种模式的。为此,我们需要引入 NamedService 接口:

    public interface NamedService {
       String name();
    }

and add abstract class:

并添加抽象类:

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

Then we create a concrete factory of specific objects like MyService:

然后我们创建一个像 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);
    }
}

where List the list of implementations of MyService interface at compile time.

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

This approach works fine if you have multiple similar factories across app that produce objects by name (if producing objects by a name suffice you business logic of course). Here map works good with String as a key, and holds all the existing implementations of your services.

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

if you have different logic for producing objects, this additional logic can be moved to some another place and work in combination with these factories (that get objects by name).

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