如何在春季使用自动装配的bean创建简单工厂模式? [英] How to create a Simple Factory Pattern with autowired beans in Spring?

查看:123
本文介绍了如何在春季使用自动装配的bean创建简单工厂模式?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个具有4种非常相似方法的控制器,该控制器在远程服务器上调用API以对不同类型的用户执行不同的操作.这些API调用之间的变化只是端点和一些参数.

I had a controller with 4 very similar methods, calling an API on a remote server to perform different actions on different types of users. What changed between these API calls are just the endpoint and some parameters.

因此,这4种方法都用非常相似的代码调用了服务:它们从服务器获取了令牌,设置了参数,返回了API的响应.由于稍后将添加更多操作,因此我决定使用使用Factory Method模式创建ServiceFactory并在服务上使用Template模式以避免代码重复.

Therefore, these 4 methods all called services with very similar code: they got a token from the server, set the parameters, return the API's response. Since more actions will be added later, I decided to use create a ServiceFactory using the Factory Method pattern and use the Template pattern on the services to avoid code duplication.

我的问题是,为了使工厂能够自动装配服务,需要将其耦合到它们,我必须在每个实现中都@Autowire.有更好的解决方案吗?

My problem is that in order for the factory to autowire the services, it needs to be coupled to them, I have to @Autowire every implementation. Is there a better solution?

这是我到目前为止的代码:

Here's the code I have so far:

@RestController
public class ActionController {
  @Autowired
  private SsoService ssoService;

  // this is the factory
  @Autowired
  private ServiceFactory factory;

  @PostMapping("/action")
  public MyResponse performAction(@RequestBody MyRequest request, HttpServletRequest req) {
    // template code (error treatment not included)
    request.setOperator(ssoService.getOperator(req));
    request.setDate(LocalDateTime.now());
    return serviceFactory.getService(request).do();
  }
}

服务工厂

@Component
public class ServiceFactory {

  @Autowired private ActivateUserService activateUserService;
  @Autowired private Action2UserType2Service anotherService;
  //etc

  public MyService getService(request) {
    if (Action.ACTIVATE.equals(request.getAction()) && UserType.USER.equals(request.getUserType()) {
      return activateUserService;
    }
    // etc
    return anotherService;
  }
}

服务库,实现MyService接口

public abstract class ServiceBase implements MyService {

  @Autowired private ApiService apiService;
  @Autowired private ActionRepository actionRepository;
  @Value("${api.path}") private String path;

  @Override
  public MyResponse do(MyRequest request) {
    String url = path + getEndpoint();
    String token = apiService.getToken();

    Map<String, String> params = getParams(request);
    // adds the common params to the hashmap

    HttpResult result = apiService.post(url, params); 
    if (result.getStatusCode() == 200) {
      // saves the performed action
      actionRepository.save(getAction());
    }
    // extracts the response from the HttpResult
    return response;
  }
}

服务实施(共有4个)

@Service
public class ActivateUserService extends ServiceBase {
  @Value("${api.user.activate}")
  private String endpoint;

  @Override
  public String getEndpoint() {
    return endpoint;
  }

  @Override
  public Map<String,String> getParams(MyRequest request) {
    Map<String, String> params = new HashMap<>();
    // adds custom params
    return params;
  }

  @Override
  public Action getAction() {
    return new Action().type(ActionType.ACTIVATED).userType(UserType.USER);
  }
}

推荐答案

您可以@Autowired MyServiceList,这将创建所有实现MyService接口的bean的List.然后,您可以向MyService添加一个接受MyRequest对象的方法,并确定该方法是否可以处理该请求.然后,您可以过滤MyServiceList以找到可以处理请求的第一个MyService对象.

You can @Autowired a List of MyService, which will create a List of all beans that implement the MyService interface. Then you can add a method to MyService that accepts a MyRequest object and decides if it can handle that request. You can then filter the List of MyService to find the first MyService object that can handle the request.

例如:

public interface MyService {

    public boolean canHandle(MyRequest request);

    // ...existing methods...
}

@Service
public class ActivateUserService extends ServiceBase {

    @Override
    public boolean canHandle(MyRequest request) {
        return Action.ACTIVATE.equals(request.getAction()) && UserType.USER.equals(request.getUserType());
    }

    // ...existing methods...
}

@Component
public class ServiceFactory {

    @Autowired
    private List<MyService> myServices;

    public Optional<MyService> getService(MyRequest request) {
        return myServices.stream()
            .filter(service -> service.canHandle(request))
            .findFirst();
    }
}

请注意,上述ServiceFactory实现使用Java 8+.如果无法使用Java 8或更高版本,则可以通过以下方式实现ServiceFactory类:

Note that the ServiceFactory implementation above uses Java 8+. If it is not possible to Java 8 or higher, you can implement the ServiceFactory class in the following manner:

@Component
public class ServiceFactory {

    @Autowired
    private List<MyService> myServices;

    public Optional<MyService> getService(MyRequest request) {

        for (MyService service: myServices) {
            if (service.canHandle(request)) {
                return Optional.of(service);
            }
        }

        return Optional.empty();
}

有关将@AutowiredList结合使用的更多信息,请参见 Autowire按类型将bean引用到列表中..

For more information on using @Autowired with List, see Autowire reference beans into list by type.

此解决方案的核心是将判断MyService实现是否可以处理MyRequest的逻辑从ServiceFactory(外部客户端)转移到MyService实现本身.

The core of this solution is moving the logic of deciding if a MyService implementation can handle a MyRequest from the ServiceFactory (an external client) to the MyService implementation itself.

这篇关于如何在春季使用自动装配的bean创建简单工厂模式?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆