Почему ApplicationProperties имеет значение null во время @PostConstruct в моем приложении Vaadin Spring Boot?

Я работаю над приложением 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, поскольку макет меню помечен как компонент, а сеанс в данный момент недоступен!

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
0
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

TL;DR: используется Spring, но не интеграция Vaadin Spring; тот Макет создается с помощью DefaultInstantiator, и внедрение изменений не выполняется. Либо конфигурация интеграции Vaadin Spring не удалась, либо произошла ошибка. не активируется или не добавляется вообще.

Кроме того, RouterLayout не должен быть одноэлементным.


Давайте разберем эту проблему:

  • Если postconstruct запускается, он должен выбросить, если appProperties есть нулевой
  • но, похоже, он работает и вызывает init, который снова вызывает initMenu
  • внутри initMenuappProperties сейчас ноль, но это окончательно и ничего, кроме оператора, не должно быть в состоянии это изменить

Любое одно должно быть правдой:

  • это не [МРЭ]
  • вывод журнала не представляет полную временную шкалу и фактически там это два разных объекта в игре

Для последнего есть объяснение:

RouterLayout косвенно управляется Spring, но напрямую через Vaadin -- посещение сайта создаст представления и их макеты для просмотр через объекты Instantiator. Если вместо этого в игре Spring из DefaultInstantiatorSpringInstantiator наконец-то сделает работа.

Но нет смысла/необходимости настраивать RouterLayout для Spring-DI и здесь это просто навредит.

  • @Lazy откладывал бы экземпляры до того момента, когда они понадобятся; Ваадин берет заботиться об этом
  • @Component делает это синглтоном и его нужно удалить
  • @DependsOn("applicationProperties") в лучшем случае бесполезен в худшем случае проблема, потому что макет материализуется из-за использования внутри аннотации @Route

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

Компонент когда-то обрабатывается Spring (всегда можно было бы заподозрить, что включая передачу appProperties). И затем Ваадин создает новый объект, но не через Spring, а через DefaultInstantiator. Этот не проходит в appProperties и позже терпит неудачу.

Спасибо за ваш ответ, если я удалю компонент, я не смогу использовать Autowire для внедрения свойств приложения в свой макет и использовать его. Я новичок в этой области и, возможно, я не полностью понял ваш ответ, можете ли вы попытаться дать пример код, чтобы проверить его, из макета, который я отправляю. Это очень поможет!

Konstantinos Gkogkos 03.09.2024 20:15

Я подозреваю, что вы не используете Vaadin-Spring-Integration; у вас есть весна, например. через Boot, и у вас есть Vaadin (но не, например, через стартер для Spring). Приведите минимально воспроизводимый пример — здесь не хватает какой-то важной части головоломки.

cfrick 04.09.2024 07:56

Спасибо, проблема решена, все, что мне нужно было сделать, это следовать вашим инструкциям и прочитать статью о том, как внедрить компонент в некомпонентный компонент в Spring-Boot!

Konstantinos Gkogkos 04.09.2024 11:50

Другие вопросы по теме