Выбор компонента Spring при автоваринге

Допустим, есть класс Animal и дочерние от него классы Dog, Cat. Есть также компоненты Spring, обрабатывающие эти классы:

@Component
class AnimalService {
   void process(Animal animal) {...}
}

@Component
class DogService extends AnimalService {
   @Override
   void process(Animal animal) {...}
}

@Component
class CatService extends AnimalService {
   @Override
   void process(Animal animal) {...}
}

В другом классе есть автоваринг AnimalService:

@Autowired
AnimalService animalService;

Можно ли сделать так, чтобы при вызове метода process, в зависимости от того, к какому подклассу принадлежит переменная animal выбирался соответствующий подкласс сервиса?


Ответы (1 шт):

Автор решения: Alexandr

Можно сделать как-то так:

interface AnimalService<A extends Animal> {
    void process(A animal);
    Class<A> getType();
}

abstract class AbstractAnimalService<A extends Animal>
        implements AnimalService<A> {
    private final Class<A> clazz;

    public AbstractAnimalService(Class<A> clazz) {
        this.clazz = clazz;
    }

    @Override
    public Class<A> getType() {
        return clazz;
    }
}

@Component
public class DogService extends AbstractAnimalService<Dog> {
    public DogService() {
        super(Dog.class);
    }

    public void process(Dog dog) {
        // Process dog
    }
}

@Component
public class CatService extends AbstractAnimalService<Cat> {
    public CatService() {
        super(Cat.class);
    }
    
    public void process(Cat cat) {
        // Process cat
    }
}

@Component
public class UniversalAnimalService implements AnimalService<Animal> {

    private final Map<Class<Animal>, AnimalService<Animal>> animalServices;

    @Autowired
    public UniversalAnimalService(
            List<AbstractAnimalService<Animal>> animalServices) {
        this.animalServices =
                animalServices.stream()
                        .collect(
                                Collectors.toMap(
                                        AnimalService::getType,
                                        as -> as));
    }

    public void process(Animal animal) {
        if (isNull(animal)) {
            return;
        }

        AnimalService<Animal> animalService =
                animalServices.get(animal.getClass());
        if (isNull(animalService)) {
            throw new IllegalArgumentException(
                    String.format(
                            "Unknown animal class [%s].",
                            animal.getClass()));
        }
        animalService.process(animal);
    }

    public Class<Animal> getType() {
        return Animal.class;
    }
}
→ Ссылка