4

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?

Steffen Harbich
  • 2,639
  • 2
  • 37
  • 71

1 Answers1

7

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

Morfic
  • 15,178
  • 3
  • 51
  • 61
  • 1
    Thanks for your detailed answer. That the order is important is weird, though. – Steffen Harbich Jul 04 '17 at 14:05
  • @SteffenHarbich yeah. But at the same time, the default Spring `@Scope` annotation is not repeatable, so you can't add 2 scopes to the same bean. However, both `@UIScope` and `@SpringView` set a scope on the view, so **I guess** one of them has to somehow take precedence... Like I said, I did not dig that far into the sources nor can I find instances/samples where multiple scopes are applied, so it's a wild guess – Morfic Jul 04 '17 at 14:16
  • This soo broken in terms of framework design! Basically the contract of Java's Annotations is broken here. By design when multiple annotations are placed on any item, these annotations should be replaceable. – sanya Apr 30 '19 at 11:03