Я работаю над приложением Vaadin с использованием Spring Boot и столкнулся с проблемой, когда ApplicationProperties имеет значение null во время метода @PostConstruct в моем классе MenuLayout. Это приводит к неправильной настройке элементов меню, и в журналах я вижу следующее предупреждение:
2024-09-03T13:09:08.059+03:00 WARN 6988 --- [io-8080-exec-10] c.r.application.layouts.MenuLayout : ApplicationProperties is null. Menu items will not be set up.
2024-09-03T13:09:08.067+03:00 INFO 6988 --- [io-8080-exec-10] c.r.a.config.ApplicationProperties : Unique Categories with Folders: {Transactions=[ObjectTransfers], Organizations=[Organization, Department], Metadata=[ResearcherComment, InfoText], Occurrences=[Event], Sources=[BookSource, OralHistorySource, ArchivalSources, WebSources, SourcePassageCollection, NewspaperPeriodical, Bibliography, SourcePassage], Entities=[Material, Persons, Objects, Collection, Route, DigitalObject, Objects]}
Вот соответствующий код для моего класса MenuLayout:
@Lazy
@Component
@DependsOn("applicationProperties")
public class MenuLayout extends HybridMenu implements RouterLayout {
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(MenuLayout.class.getName());
private final ApplicationProperties appProperties;
@Autowired
public MenuLayout(@Qualifier("applicationProperties") ApplicationProperties appProperties) {
this.appProperties = appProperties;
}
@PostConstruct
public void postConstruct() {
// Check if ApplicationProperties is null
if (this.appProperties == null) {
logger.log(Level.SEVERE, "ApplicationProperties is null in postConstruct of MenuLayout.");
throw new IllegalStateException("ApplicationProperties is null in postConstruct of MenuLayout.");
}
// Ensure VaadinSession is available
VaadinSession vaadinSession = VaadinSession.getCurrent();
if (vaadinSession == null) {
throw new IllegalStateException("VaadinSession is not available.");
}
// Initialize the menu
init(vaadinSession, UI.getCurrent());
}
@Override
public boolean init(VaadinSession vaadinSession, UI ui) {
initMenu();
return true;
}
private void initMenu() {
withConfig(new MenuConfig());
getConfig().setTheme(ETheme.Lumo);
if (appProperties != null) {
setupMenuItems();
} else {
logger.log(Level.WARNING, "ApplicationProperties is null. Menu items will not be set up.");
}
}
private void setupMenuItems() {
// Menu setup code...
}
}
это свойства приложения:
package com.ricontrans.application.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
@Primary
@Component
@ConfigurationProperties(prefix = "application")
public class ApplicationProperties {
private final Map<String, Category> acceptedCategories = new HashMap<>();
private final List<Description> descriptions = new ArrayList<>();
private static final Logger logger = Logger.getLogger(ApplicationProperties.class.getName());
public ApplicationProperties() {
logger.log(Level.INFO, "ApplicationProperties constructor called.");
loadProperties();
}
private void loadProperties() {
logger.log(Level.INFO, "Loading properties from configuration.properties.");
Properties properties = new Properties();
try (InputStream input = getClass().getClassLoader().getResourceAsStream("configuration.properties")) {
if (input == null) {
logger.log(Level.WARNING, "configuration.properties not found.");
return;
}
properties.load(input);
// Load accepted categories (existing functionality)
properties.forEach((key, value) -> {
String keyString = (String) key;
String valueString = (String) value;
String[] keyParts = keyString.split("\\.");
if (keyParts.length == 4 && "acceptedCategories".equals(keyParts[1])) {
String categoryName = keyParts[2];
String property = keyParts[3];
// Ensure that the category exists
Category category = acceptedCategories.computeIfAbsent(categoryName, k -> new Category());
// Update the category based on the property
switch (property) {
case "folder":
category.setFolder(valueString);
break;
case "schema":
category.setSchema(valueString);
break;
case "label":
category.setLabel(valueString);
break;
case "icon":
category.setIcon(valueString);
break;
case "tableProperties":
category.setTableProperties(valueString);
break;
case "Pie":
category.setPie(valueString);
break;
case "PieCategories":
category.setPieCategories(valueString);
break;
case "Map":
category.setMap(valueString);
break;
case "Time":
category.setTime(valueString);
break;
case "chartTypes":
category.setChartTypes(valueString);
break;
case "category":
category.setCategory(valueString);
break;
default:
break;
}
}
});
logger.log(Level.INFO, "Accepted Categories: {0}", acceptedCategories);
// Load descriptions for texts and images (new functionality)
int index = 1;
while (true) {
String textPath = properties.getProperty("application.description." + index + ".text");
String imagePath = properties.getProperty("application.description." + index + ".image");
if (textPath == null && imagePath == null) {
break; // No more descriptions to load
}
Description description = new Description();
if (textPath != null) {
description.setTextPath(textPath);
description.setLeft(Boolean.parseBoolean(properties.getProperty("application.description." + index + ".text.left", "false")));
description.setCenter(Boolean.parseBoolean(properties.getProperty("application.description." + index + ".text.center", "false")));
description.setRight(Boolean.parseBoolean(properties.getProperty("application.description." + index + ".text.right", "false")));
}
if (imagePath != null) {
description.setImagePath(imagePath);
description.setLeft(Boolean.parseBoolean(properties.getProperty("application.description." + index + ".image.left", "false")));
description.setCenter(Boolean.parseBoolean(properties.getProperty("application.description." + index + ".image.center", "false")));
description.setRight(Boolean.parseBoolean(properties.getProperty("application.description." + index + ".image.right", "false")));
}
description.setAlignWithLast(Boolean.parseBoolean(properties.getProperty("application.description." + index + ".alignWithLast", "false")));
descriptions.add(description);
index++;
}
logger.log(Level.INFO, "Descriptions loaded: {0}", descriptions);
} catch (IOException ex) {
logger.log(Level.SEVERE, "IOException occurred while loading properties", ex);
}
}
public Map<String, Category> getAcceptedCategories() {
return acceptedCategories;
}
public List<Description> getDescriptions() {
return descriptions;
}
public String getCategoryFolder(String categoryName) {
Category category = acceptedCategories.get(categoryName);
return category != null ? category.getFolder() : null;
}
public Map<String, List<String>> getUniqueCategoriesWithFolders() {
Map<String, List<String>> categoryFoldersMap = new HashMap<>();
acceptedCategories.values().forEach(category -> {
String categoryName = category.getCategory();
String folder = category.getFolder();
if (categoryName != null && folder != null) {
// Get the list of folders for this category, or create a new one if it doesn't exist
List<String> folders = categoryFoldersMap.computeIfAbsent(categoryName, k -> new ArrayList<>());
// Add the folder to the list
folders.add(folder);
}
});
logger.log(Level.INFO, "Unique Categories with Folders: {0}", categoryFoldersMap);
return categoryFoldersMap;
}
public static class Category {
private String folder;
private String schema;
private String label;
private String icon;
private String tableProperties;
private String Pie;
private String PieCategories;
private String Time;
private String Map;
private String chartTypes;
private String category;
private Properties properties = new Properties();
// Getters and setters
public String getFolder() {
return folder;
}
public void setFolder(String folder) {
this.folder = folder;
}
public String getSchema() {
return schema;
}
public void setSchema(String schema) {
this.schema = schema;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public String getIcon() {
return icon;
}
public void setIcon(String icon) {
this.icon = icon;
}
public String getTableProperties() {
return tableProperties;
}
public void setTableProperties(String tableProperties) {
this.tableProperties = tableProperties;
}
public String getPie() {
return Pie;
}
public void setPie(String pie) {
Pie = pie;
}
public String getPieCategories() {
return PieCategories;
}
public void setPieCategories(String pieCategories) {
PieCategories = pieCategories;
}
public String getTime() {
return Time;
}
public void setTime(String time) {
Time = time;
}
public String getMap() {
return Map;
}
public void setMap(String map) {
Map = map;
}
public String getChartTypes() {
return chartTypes;
}
public void setChartTypes(String chartTypes) {
this.chartTypes = chartTypes;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
@Override
public String toString() {
return "Category{" +
"folder='" + folder + '\'' +
", schema='" + schema + '\'' +
", label='" + label + '\'' +
", icon='" + icon + '\'' +
", tableProperties='" + tableProperties + '\'' +
", Pie='" + Pie + '\'' +
", PieCategories='" + PieCategories + '\'' +
", Time='" + Time + '\'' +
", Map='" + Map + '\'' +
", chartTypes='" + chartTypes + '\'' +
", category='" + category + '\'' +
'}';
}
}
public static class Description {
private String textPath;
private String imagePath;
private boolean left;
private boolean center;
private boolean right;
private boolean alignWithLast;
private String image;
private String text;
// Getters and setters
public String getTextPath() {
return textPath;
}
public void setTextPath(String textPath) {
this.textPath = textPath;
}
public String getImagePath() {
return imagePath;
}
public void setImagePath(String imagePath) {
this.imagePath = imagePath;
}
public boolean isLeft() {
return left;
}
public void setLeft(boolean left) {
this.left = left;
}
public boolean isCenter() {
return center;
}
public void setCenter(boolean center) {
this.center = center;
}
public boolean isRight() {
return right;
}
public void setRight(boolean right) {
this.right = right;
}
public boolean isAlignWithLast() {
return alignWithLast;
}
public void setAlignWithLast(boolean alignWithLast) {
this.alignWithLast = alignWithLast;
}
@Override
public String toString() {
return "Description{" +
"textPath='" + textPath + '\'' +
", imagePath='" + imagePath + '\'' +
", left = " + left +
", center = " + center +
", right = " + right +
", alignWithLast = " + alignWithLast +
'}';
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
}
Основная функциональность, которую я пытаюсь достичь прямо здесь, заключается в том, что я могу установить гибридное меню с динамическим макетом для всех моих представлений, а информация о опции меню задается в файле Configuration.properties, где загрузка этих свойств. сделано в ApplicationProperties.java! Итак, я предполагаю, что проблема здесь в том, что по какой-то причине макет меню всегда инициализируется первым из applicationProperties, потому что они оба являются компонентами, и, возможно, сначала нужно что-то сделать.
Как видите, я пробовал много вещей:
Ленивая загрузка
Зависит от свойств приложения
Пост Конструкт.
Кроме того, если я удалю метод конструкции post, у меня возникнет проблема с VaadinSession, который имеет значение null, поскольку макет меню помечен как компонент, а сеанс в данный момент недоступен!
TL;DR: используется Spring, но не интеграция Vaadin Spring; тот
Макет создается с помощью DefaultInstantiator
, и внедрение изменений не выполняется.
Либо конфигурация интеграции Vaadin Spring не удалась, либо произошла ошибка.
не активируется или не добавляется вообще.
Кроме того, RouterLayout
не должен быть одноэлементным.
Давайте разберем эту проблему:
postconstruct
запускается, он должен выбросить, если appProperties
есть
нулевойinit
, который снова вызывает
initMenu
initMenu
appProperties
сейчас ноль, но это окончательно и
ничего, кроме оператора, не должно быть в состоянии это изменитьЛюбое одно должно быть правдой:
Для последнего есть объяснение:
RouterLayout
косвенно управляется Spring, но напрямую через Vaadin
-- посещение сайта создаст представления и их макеты для
просмотр через объекты Instantiator
. Если вместо этого в игре Spring
из DefaultInstantiator
SpringInstantiator
наконец-то сделает
работа.
Но нет смысла/необходимости настраивать RouterLayout
для Spring-DI и
здесь это просто навредит.
@Lazy
откладывал бы экземпляры до того момента, когда они понадобятся; Ваадин берет
заботиться об этом@Component
делает это синглтоном и его нужно удалить@DependsOn("applicationProperties")
в лучшем случае бесполезен
в худшем случае проблема, потому что макет материализуется из-за использования
внутри аннотации @Route
Так что @Component
должно значительно ухудшить ситуацию (при перезагрузке произойдет
могут быть ошибки, что на компоненты уже есть ссылки из другого пользовательского интерфейса).
Поскольку проблема не в этом, это убедительно указывает на два
теория различных случаев.
Компонент когда-то обрабатывается Spring (всегда можно было бы заподозрить, что
включая передачу appProperties
). И затем Ваадин создает новый
объект, но не через Spring, а через DefaultInstantiator
. Этот
не проходит в appProperties
и позже терпит неудачу.
Я подозреваю, что вы не используете Vaadin-Spring-Integration; у вас есть весна, например. через Boot, и у вас есть Vaadin (но не, например, через стартер для Spring). Приведите минимально воспроизводимый пример — здесь не хватает какой-то важной части головоломки.
Спасибо, проблема решена, все, что мне нужно было сделать, это следовать вашим инструкциям и прочитать статью о том, как внедрить компонент в некомпонентный компонент в Spring-Boot!
Спасибо за ваш ответ, если я удалю компонент, я не смогу использовать Autowire для внедрения свойств приложения в свой макет и использовать его. Я новичок в этой области и, возможно, я не полностью понял ваш ответ, можете ли вы попытаться дать пример код, чтобы проверить его, из макета, который я отправляю. Это очень поможет!