Search code examples
springvaadin8

@UIScope annotation not respected for spring view?


I am facing an issue with the Vaadin spring annotation @UIScope, defined as follows:

@SpringComponent
@SpringView(name = AdminView.VIEW_NAME)
@UIScope
public class AdminView extends NavigatingView {
    ...
}

The view is created every time the navigation is opening the view. I would expect that it is created only once, on first time access.

However, if I replace @UIScope with @Scope(UIScopeImpl.VAADIN_UI_SCOPE_NAME) then it works as expected. Did I miss something?


Solution

  • It's related to the order of the @SpringView and @UIScope annotations, as the tutorial and the older wiki page briefly suggest:

    // Pay attention to the order of annotations

    It's probably related to how and when the annotations are processed. I did not dig that deep into the Vaadin code, but as per the the @SpringView javadoc it puts the view into a view-scope by default. Furthermore, I don't think you require the @SpringComponent annotation because you're already using @SpringView to register it a spring component.

    Annotation to be placed on View-classes that should be handled by the SpringViewProvider.

    This annotation is also a stereotype annotation, so Spring will automatically detect the annotated classes. By default, this annotation also puts the view into the view scope. You can override this by using another scope annotation, such as the UI scope, on your view class. However, the singleton scope will not work!

    In the sample below, you'll find 2 views, the first one with the annotations in the correct order, and the second one with them swapped:

    @SpringUI
    @SpringViewDisplay
    public class MyVaadinUI extends UI implements ViewDisplay {
    
        /* UI */
    
        private Panel springViewDisplay;
    
        @Override
        protected void init(VaadinRequest request) {
            VerticalLayout mainLayout = new VerticalLayout();
            HorizontalLayout buttonLayout = new HorizontalLayout();
            springViewDisplay = new Panel();
    
            buttonLayout.addComponent(new Button("1", event -> getNavigator().navigateTo(FirstView.VIEW_NAME)));
            buttonLayout.addComponent(new Button("2", event -> getNavigator().navigateTo(SecondView.VIEW_NAME)));
            mainLayout.addComponents(buttonLayout, springViewDisplay);
            setContent(mainLayout);
    
        }
    
        @Override
        public void showView(View view) {
            springViewDisplay.setContent((Component) view);
        }
    
    
        /* VIEWS */
    
        @UIScope
        @SpringView(name = FirstView.VIEW_NAME)
        public static class FirstView extends HorizontalLayout implements View {
    
            public static final String VIEW_NAME = "";
    
            @PostConstruct
            private void init() {
                System.out.println("Created first view");
                addComponent(new Label("First view - " + LocalDateTime.now()));
            }
    
            @Override
            public void enter(ViewChangeListener.ViewChangeEvent event) {
                // no-op
            }
        }
    
        @SpringView(name = SecondView.VIEW_NAME)
        @UIScope
        public static class SecondView extends HorizontalLayout implements View {
    
            public static final String VIEW_NAME = "secondView";
    
            @PostConstruct
            private void init() {
                System.out.println("Created second view");
                addComponent(new Label("Second view - " + LocalDateTime.now()));
            }
    
            @Override
            public void enter(ViewChangeListener.ViewChangeEvent event) {
                // no-op
            }
        }
    }
    

    As you'll notice in the animation below, when navigating to the second view a new instance is always created, while navigating to the first one will reuse the initial instance:

    vaadin-view-uiscope