简体   繁体   English

如何在 Spring 中使用自动装配的 bean 创建简单的工厂模式?

[英]How to create a Simple Factory Pattern with autowired beans in Spring?

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.我有一个具有 4 个非常相似方法的控制器,在远程服务器上调用 API 以对不同类型的用户执行不同的操作。 What changed between these API calls are just the endpoint and some parameters.这些 API 调用之间发生的变化只是端点和一些参数。

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.因此,这 4 个方法都使用非常相似的代码调用服务:它们从服务器获取令牌、设置参数、返回 API 的响应。 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.由于稍后将添加更多操作,我决定使用工厂方法模式创建一个 ServiceFactory 并在服务上使用模板模式以避免代码重复。

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.我的问题是,为了让工厂自动装配服务,它需要与它们耦合,我必须@Autowire每个实现。 Is there a better solution?有更好的解决方案吗?

Here's the code I have so far:这是我到目前为止的代码:

Rest Controller休息控制器

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

Service Factory服务工厂

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

Service Base, implementing the MyService interface Service Base,实现 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;
  }
}

Service Implementation (there are 4)服务实现(有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);
  }
}

You can @Autowired a List of MyService , which will create a List of all beans that implement the MyService interface.您可以@Autowired一个MyService List ,它将创建一个实现MyService接口的所有 bean 的List Then you can add a method to MyService that accepts a MyRequest object and decides if it can handle that request.然后,您可以向MyService添加一个方法,该方法接受一个MyRequest对象并决定它是否可以处理该请求。 You can then filter the List of MyService to find the first MyService object that can handle the request.然后,您可以过滤MyService List以找到可以处理请求的第一个MyService对象。

For example:例如:

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

Note that the ServiceFactory implementation above uses Java 8+.请注意,上面的ServiceFactory实现使用 Java 8+。 If it is not possible to Java 8 or higher, you can implement the ServiceFactory class in the following manner:如果 Java 8 或更高版本无法实现,您可以通过以下方式实现ServiceFactory类:

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

For more information on using @Autowired with List , see Autowire reference beans into list by type .有关将@AutowiredList一起使用的更多信息,请参阅Autowire 按类型将 beans 引用到 list 中


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.这个解决方案的核心是移动决定是否一个的逻辑MyService实现可以处理MyRequestServiceFactory (外部客户端)连接到MyService实现本身。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM