Spring динамическая инъекция, factory -образный рисунок
Продолжение из инъекции зависимостей, замедленной инъекции. У меня есть класс Main:
package test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Scanner;
@Component
public class Main {
@Autowired
private StringValidator stringValidator;
@Autowired
private StringService stringService;
@Autowired
private ValidationService validationService;
public void main() {
scanKeyboardCreateLists();
stringValidator.validate();
final List<String> validatedList = stringValidator.getValidatedList();
for (String currentValid : validatedList) {
System.out.println(currentValid);
}
}
private void scanKeyboardCreateLists() {
//Let presume the user interacts with the GUI, dynamically changing the object graph...
//Needless to say, this is past container initialization...
Scanner scanner = new Scanner(System.in);
int choice = scanner.nextInt();
//Delayed creation, dynamic
if (choice == 0) {
stringService.createList();
validationService.createList();
} else {
stringService.createSecondList();
validationService.createSecondList();
}
}
public static void main(String[] args) {
ApplicationContext container = new ClassPathXmlApplicationContext("/META-INF/spring/applicationContext.xml");
container.getBean(Main.class).main();
}
}
И диаграмма объекта динамически создается в зависимости от взаимодействия пользователя. Я решил использовать приложение, позволяющее мне это проверить очень просто. Кроме того, поскольку списки поддерживаются контейнером, динамическая природа этого приложения (и любого другого) не имеет значения, поскольку они могут быть запрошены в любое время, когда приложение им нужно, поддерживая их элементы.
Остальная часть кода находится здесь:
package test;
import java.util.List;
public interface Stringable {
List<String> getStringList();
}
package test;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
@Component
public class StringList extends ArrayList<String> {
}
package test;
import org.springframework.stereotype.Component;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;
@Component
public class StringService implements Stringable {
private List<String> stringList;
@Inject
public StringService(final ArrayList<String> stringList) {
this.stringList = stringList;
}
//Simplified
public void createList() {
stringList.add("FILE1.txt");
stringList.add("FILE1.dat");
stringList.add("FILE1.pdf");
stringList.add("FILE1.rdf");
}
public void createSecondList() {
stringList.add("FILE2.txt");
stringList.add("FILE2.dat");
stringList.add("FILE3.pdf");
stringList.add("FILE3.rdf");
}
@Override
public List<String> getStringList() {
return stringList;
}
}
package test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component
public class StringValidator {
private List<String> stringList;
private List<String> validationList;
private final List<String> validatedList = new ArrayList<String>();
@Autowired
public StringValidator(final ArrayList<String> stringList,
final ArrayList<String> validationList) {
this.stringList = stringList;
this.validationList = validationList;
}
public void validate() {
for (String currentString : stringList) {
for (String currentValidation : validationList) {
if (currentString.equalsIgnoreCase(currentValidation)) {
validatedList.add(currentString);
}
}
}
}
public List<String> getValidatedList() {
return validatedList;
}
}
package test;
import java.util.List;
public interface Validateable {
List<String> getValidationList();
}
package test;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
@Component
public class ValidationList extends ArrayList<String> {
}
package test;
import org.springframework.stereotype.Component;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;
@Component
public class ValidationService implements Validateable {
private List<String> validationList;
@Inject
public ValidationService(final ArrayList<String> validationList) {
this.validationList = validationList;
}
//Simplified...
public void createList() {
validationList.add("FILE1.txt");
validationList.add("FILE2.txt");
validationList.add("FILE3.txt");
validationList.add("FILE4.txt");
}
public void createSecondList() {
validationList.add("FILE5.txt");
validationList.add("FILE6.txt");
validationList.add("FILE7.txt");
validationList.add("FILE8.txt");
}
@Override
public List<String> getValidationList() {
return validationList;
}
}
Кто-нибудь знает, как я могу решить вызов метода createList() или createSecondList() - без использования конструктора, который в значительной степени заставляет дизайн. Я думал о factory, но factory для каждого класса в проекте большей величины не кажется хорошей идеей.
Что-то вроде:
<bean ... factory-method="..." depends-on="..." lazy-init="..."/>
И в методе factory создайте экземпляр класса и вызовите метод createList().
Или вызовите его так, используя некоторый метод, который снова выглядит плохо, заставляя метод нести ответственность за создание экземпляра графа объекта.
Рисунок зависимостей времени выполнения, которые я хочу разрешить во время выполнения, приведен ниже:
![enter image description here]()
Есть ли другой способ использования контейнера для достижения динамической ленивой инициализации в зависимости от взаимодействия пользователя?
Спасибо.
Ответы
Ответ 1
Если вы хотите, чтобы какой-то член вашего класса был динамически инициализирован\заполнен при каждом вызове соответствующего получателя, вы можете попробовать метод Injection метода поиска. Прочитайте с. 3.3.4.1 здесь.
Таким образом, даже если класс, содержащий динамический элемент, был создан в scope=singletone
(по умолчанию для контейнера spring bean) каждый раз, когда вы присоединяетесь к полю, у которого есть метод поиска, вы получите соответствующий объект в соответствии с бизнес-логикой, реализованной внутри метода поиска. В вашем случае список представляет собой интерфейс, поэтому вы можете легко выполнить проверку внутри метода поиска и вернуть проверенный список.
Edit:
Я нашел лучше пример в документации spring - я думаю, это очень ясно.
Взгляните на "3.4.6.1 Поиск метода инъекции"
При настройке класса Main
назначьте метод поиска его члену List
- он будет вызываться всякий раз, когда вам нужен новый экземпляр List
bean.
Удачи!
Ответ 2
Spring предназначен для повторного использования инъекций компонентов, а не для обработки бизнес-данных и инъекций.
Действительно, некоторые данные используются в инъекции зависимостей, но только для настройки поведения компонентов, а не для создания владельца бизнес-данных.
Кстати, в вашем случае может быть использована следующая опция: спасибо BeanFactory
с интерфейс BeanFactoryAware и использование области = "prototype", вы можете сгенерировать bean, вызвав getBean()
, как в этот пример, или из что другой вопрос: создание bean по запросу.
Альтернативный вариант, если у вас есть ограниченное количество beans для подготовки, заключается в использовании родового bean создания того же пути, который отсутствует beans, издеваются
Теперь рассмотрим, что Spring никогда мусор не собирает beans в своем Контексте. Поэтому для хранения памяти опасно создавать Spring beans для хранения бизнес-данных.
Если ваша цель отличается (надеюсь, так), возможно, вы пытаетесь реализовать свою собственную поддержку нескольких арендаторов. Spring предоставляет аренду, если у вас есть другой бизнес-контекст для реализации с конкретными компонентами или поведением.
Ответ 3
Похоже, пользователь может выбрать графики 1..N объектов, и вы хотите загрузить только тот, который пользователь выбирает во время выполнения. Если графики известны во время разработки, но пользователь просто выбирает тот, который они хотят, то это звучит для меня, как то, что у вас есть, это набор приложений ApplicationContexts, и вы хотите загрузить только один ApplicationContext, который пользователь выбирает во время выполнения. Итак, почему бы просто не определить набор ApplicationContexts, а затем просто создать экземпляр правильного во время выполнения. Поскольку Spring поддерживает Java Config, имеет смысл определить эти конфиги как классы Java, чтобы вы могли получить наследование и не разрезать/вставлять какой-либо код.