Преобразование автоматически в централизованный компонент для нескольких объектов домена

Я создаю проект, который будет реагировать на сбор нескольких объектов bean, сохранить его в базе данных и вернуть статус транзакции. Может быть несколько объектов, которые могут быть отправлены клиентом. Для каждого объекта они имеют отдельную базу данных, таким образом, отдельный контроллер.

Поэтому я планировал создать фреймворк, который может принимать несколько объектов из нескольких контроллеров и отправлять только один централизованный объект. Но я не уверен, как использовать централизованный объект в качестве возвращаемого типа в контроллере (в настоящее время я возвращал их как Object). Ниже мой код:

контроллер:

@RestController
@RequestMapping("/stat/player")
public class PlayerController {

    @Autowired
    private StatService<PlayerValue> statPlayer;

    @RequestMapping("/number/{number}")
    public Object findByNumber(@PathVariable String number) { // Here returning Object seem odd
        return statPlayer.findByNumber(number);
    }
}

Обслуживание:

@Service
@Transactional(isolation = Isolation.READ_COMMITTED)
public class PlayerServiceImpl implements StatService<PlayerValue> {

    @Autowired
    private PlayerRepository repository;

    @Override
    public PlayerValue findByNumber(String number) {
        Optional<PlayerEntity> numberValue = repository.findByNumber(number);
        return numberValue.map(PlayerEntity::toValue).orElse(null);
    }
}

В службе я вернул объект PlayerValue но я хочу, чтобы этот объект был PlayerValue в централизованный компонент ResponseValue. Я создал для этого

@Aspect
@Component
public class Converter {
    private static final Logger LOG = LoggerFactory.getLogger(Converter.class);

    @Pointcut("within(@org.springframework.web.bind.annotation.RestController *)")
    public void restControllerClassMethod() {}

    private <T> ResponseValue<T> convert(List<T> results) {
        String message = results.isEmpty() ? "No result found" : ResponseValueStatus.OK.toString();

        return new ResponseValue<>(ResponseValueStatus.OK, message, results);
    }

    @Around("restControllerClassMethod()")
    @SuppressWarnings("unchecked")
    public <T> ResponseValue<T> convert(ProceedingJoinPoint joinPoint) {
        ResponseValue value;
        try {
            Object findObject = joinPoint.proceed();
            List<Object> objects = toList(findObject);
            value = convert(objects);
        } catch (NullPointerException e) {
            throw new StatException(String.format("Exception thrown from %s from %s method with parameter %s", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(), joinPoint.getArgs()[0].toString()));
            //this exception will go in a controller advice and create a response value with this message
        } catch (Throwable e) {
            LOG.error("Exception occurred while converting the object", e);
            throw new StatException(String.format("Exception thrown from %s from %s method with parameter %s with exception message %s", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(), joinPoint.getArgs()[0].toString(), e.getMessage()));
        }
        return value;
    }

    private List<Object> toList(Object findObject) {
        List<Object> objects = new ArrayList<>();
        if (findObject instanceof List) {
            ((List) findObject).forEach(item -> objects.add(findObject));
        } else {
            objects.add(findObject);
        }
        return objects;
    }
}

Подводя итог, может существовать несколько объектов, похожих на PlayerValue. Мне нужен способ вернуть результат в централизованном компоненте. Над работой над процессом, НО для этого я должен предоставить возвращаемый тип как Object in Controller. У кого-нибудь есть идея, как я могу использовать возвращаемый тип как List или T в контроллере. Также я знаю, что это можно сделать, реализовав интерфейс ValueConverter, но это преобразование является простым. Поэтому было бы неплохо, если бы любой другой разработчик не должен был внедрять ValueConverter каждый раз, когда он хочет добавить другой контроллер.

Также не стесняйтесь ознакомиться с реализацией и дайте мне знать, есть ли у кого-нибудь альтернативные идеи или комментарии.

Примечание. Я уменьшаю количество кода в вопросе, чтобы его было легче понять, не понимая контекста фактических требований. Пожалуйста, дайте мне знать, если кому-то нужна дополнительная информация.

Ответ 1

После некоторых исследований я столкнулся с лучшим дизайнерским решением для фреймворка (но, конечно, с недостатками), чтобы добиться преобразования в централизованный компонент для нескольких доменных объектов - использовать интерфейс маркера.

Интерфейс маркера может обеспечить централизованный тип для всех компонентов. Следующее правило должно сопровождаться тем, что клиент должен реализовать этот интерфейс маркера. Таким образом, основное решение

Интерфейс маркера:

public interface StateResponseValue<T> {
}

Внедрите интерфейс во всех компонентах.

public class PlayerValue implements StateResponseValue<PlayerValue> {
}

public class ResponseValue<T> implements StateResponseValue<T> {

    //fields and their getter and setter
}

Измените тип возврата в службе и контроллере.

public interface StatService<T> {
    StateResponseValue<T> findByNumber(String number);
}

Изменение типа возврата в контроллере

@RestController
@RequestMapping("/stat/player")
public class PlayerController {

    @Autowired
    private StatService<PlayerValue> statPlayer;

    @RequestMapping("/number/{number}")
    public StateResponseValue<T> findByNumber(@PathVariable String number) { // Here returning Object seem odd
        return statPlayer.findByNumber(number);
    }
}

Примечание. Главный недостаток, который я чувствую, заключается в том, что всякий раз, когда мы хотим получить доступ к клиенту поля, необходимо явно передать объект ResponseValue который все еще довольно уродлив.

Ответ 2

Что делать, если вы создаете генератор AbstractStatController который является общим?

Общий интерфейс StatService:

public interface StatService<T> {
    T findByNumber(String number);
}

Общий абстрактный класс AbstractStatController:

public abstract class AbstractStatController<T> {

    abstract StatService<T> getStatService();

    @RequestMapping("/number/{number}")
    public T findByNumber(@PathVariable String number) {
        return getStatService().findByNumber(number);
    }

}

Конкретный класс PlayerController:

@RestController
@RequestMapping("/stat/player")
public class PlayerController extends AbstractStatController<Player> {

    private final PlayerService playerService;

    public PlayerController(PlayerService playerService) {
        this.playerService = playerService;
    }

    @Override
    StatService<Player> getStatService() {
        return playerService;
    }
}