Внедрение типа класса в конструктор компонента Spring

Вопрос или проблема

По причинам обобщенности у меня есть некоторые компоненты, которые имеют следующий телесный вид:

@Component
public class MyClass<C> extends SomeAbstractClass<A, C> {

    public MyClass(SomeClass1 someClass1, SomeClass2 someClass2) {
        super(superMethod(A.class, C.class));
    }
}

MyClass – это просто обобщенный каркас моих классов, они могут или не могут иметь какие-либо объекты в конструкторе (представленные SomeClass1 и SomeClass2). Абстрактный суперкласс принимает два класса, здесь A указан явно, он не является обобщенным, поэтому A.class является действительным. Но C.class не является действительным, поскольку это обобщенный класс.

Поэтому я попробовал вот это:

@Component
public class MyClass<C> extends SomeAbstractClass<A, C> {

    public MyClass(SomeClass1 someClass1, SomeClass2 someClass2, Class<C> clazz) {
        super(superMethod(A.class, clazz));
    }
}

Этот код компилируется, но Spring не может внедрить Class<C> clazz, как я могу это сделать?

Редактирование:
Я также пробовал что-то, что работает, но мне это не очень нравится. Я создаю класс для каждого обобщенного типа, как таковой:

@Component
public class MyClassWithoutGeneric extends SomeAbstractClass<A, B> {

    public MyClassWithoutGeneric(SomeClass1 someClass1, SomeClass2 someClass2) {
        super(superMethod(A.class, B.class));
    }
}

То, что мне не нравится в этом решении, это то, что оно не очень универсально и не поддерживаемо.

Спасибо

Я нашел решение, которое мне больше нравится:

Теперь у меня есть обертка класса MyClassFactory, которая имеет необходимые зависимости и выглядит примерно так:


@Component
public class MyClassFactory {

    private final SomeClass1 someClass1;
    private final SomeClass2 someClass2;

    public MyClassFactory(SomeClass1 someClass1, SomeClass2 someClass2) {
        this.someClass1 = someClass1;
        this.someClass2 = someClass2;
    }

    public <C> MyClass<C> create(Class<C> clazz) {
        return new MyClass<>(someClass1, someClass2, clazz);
    }
}

Ответ или решение

Вам необходимо создать обобщенные классы в Spring, при этом сталкиваясь с проблемой внедрения типов, связанных с параметрами типа. Как вы уже заметили, прямое внедрение Class<C> в конструктор вашего класса MyClass<C> не работает в контексте Spring, поскольку контейнер не знает, какой конкретный тип C необходимо использовать при создании экземпляра MyClass.

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

Решение через фабричный класс

Ваш фабричный класс MyClassFactory позволяет инкапсулировать зависимость от SomeClass1 и SomeClass2, а также предоставляет метод для создания экземпляров MyClass<C> с заданным типом. Вот как это выглядит:

@Component
public class MyClassFactory {

    private final SomeClass1 someClass1;
    private final SomeClass2 someClass2;

    public MyClassFactory(SomeClass1 someClass1, SomeClass2 someClass2) {
        this.someClass1 = someClass1;
        this.someClass2 = someClass2;
    }

    public <C> MyClass<C> create(Class<C> clazz) {
        return new MyClass<>(someClass1, someClass2, clazz);
    }
}

Также ваш MyClass остаётся обобщённым классом:

@Component
public class MyClass<C> extends SomeAbstractClass<A, C> {

    public MyClass(SomeClass1 someClass1, SomeClass2 someClass2, Class<C> clazz) {
        super(superMethod(A.class, clazz));
    }
}

Использование фабрики

Теперь, чтобы создать экземпляр MyClass, вам нужно будет сделать это через MyClassFactory. Пример использования может выглядеть так:

@Autowired
private MyClassFactory myClassFactory;

public void someMethod() {
    MyClass<SomeConcreteClass> instance = myClassFactory.create(SomeConcreteClass.class);
    // Используйте instance по мере необходимости
}

Другие рекомендации

  1. Параметры типа в Spring: Иногда параметры типа могут быть переданы через @Qualifier в Spring, если вы используете разные реализации одного интерфейса. Однако это не решает вашу проблему с обобщёнными классами напрямую.

  2. Использование рефлексии: Если вам действительно необходимо сделать что-то более динамичное, можно рассмотреть возможность использования рефлексии для инициализации классов во время выполнения. Однако это увеличит сложность и может негативно повлиять на читаемость кода.

  3. Создание специализированных фабрик: В зависимости от вашей логики, вы можете создавать специализированные фабрики для некоторых типичных реализаций C, что упростит создание объектов.

Заключение

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

Оцените материал
Добавить комментарий

Капча загружается...