【问题标题】:How to create a Simple Factory Pattern with autowired beans in Spring?如何在 Spring 中使用自动装配的 bean 创建简单工厂模式?
【发布时间】:2019-01-10 17:50:56
【问题描述】:

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

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

我的问题是,为了让工厂自动装配服务,它需要与它们耦合,我必须@Autowire 每次实现。有没有更好的解决方案?

这是我目前的代码:

休息控制器

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

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

服务实现(有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);
  }
}

【问题讨论】:

  • 似乎您担心在ServiceFactory 中注入许多UserService 实现,但从您描述的内容和您发布的代码来看,似乎所有4 个MyService 实现实际上都有相同的逻辑,不同之处只是它们的配置数据。如果是,您可以创建一个接受配置参数的单个实现 ....
  • @KenChan 啊,很抱歉这么久才回复您的评论。确实,如果只是配置更改,最好使用配置参数。但这并不是唯一的区别——尽管非常相似,getParams 方法中的实现也发生了变化。因此,拥有一个服务也需要大量的 ifs,或者根据执行的操作使用策略模式——这与我接受的解决方案类似。

标签: java spring factory-method


【解决方案1】:

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

例如:

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 类:

@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 reference beans into list by type


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

【讨论】:

  • 有趣,我真的很喜欢这个解决方案,因为它将决定哪个服务应该处理请求的决定推迟到服务本身,非常感谢!
  • 净化空气。非常需要。感谢您抽出宝贵时间并为社区分享。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多