介绍

Vaadin的工作方式依赖于服务器端渲染,因此可以自然地集成到诸如Spring之类的框架中。 Vaadin的Spring集成已经存在了一段时间,并且提供了用于在Spring容器中配置和管理Vaadin的工具,如果您希望将Vaadin与Spring Boot结合使用,那么您会很幸运,因为Vaadin的人已经做了创建可以自动配置几乎所有内容的启动程序的工作,以便您可以在几秒钟内启动并运行一个简单的UI。 在本文中,我们将简要介绍如何在Spring boot中使用Vaadin。

设定

创建Spring引导应用程序的最佳方法是使用Spring Initializr 。 我们将检查Vaadin以及其他标准的Spring起动器,例如Web和Security,然后单击“ Generate Project”。

springboot 方法中Validated调用 vaddin springboot_vue

要在上下文路径的根部创建视图,只需创建一个扩展UI的类并使用@SpringUI进行批注就@SpringUI

@SpringUI
public class Main extends UI {

    @Override
    protected void init(VaadinRequest vaadinRequest) {
        setContent(new Label("Hello"));
    }
}

如果要使用的路径与根目录不同,则可以使用path属性: @SpringUI(path="/app")

用户界面和视图

Vaadin的用户界面概念类似于SPA(单页应用程序)的概念。 UI类被视为多个视图的根容器。 视图可以看作是UI的特定状态。 一个应用程序可以具有多个UI类,但是建议使用一个具有多个视图的UI ,因为它效率更高。 借助Navigator ,可以在不离开页面或UI情况下从一个视图配置到另一个视图。 要创建一个View ,我们只需要实现视图接口并用@SpringView对其进行注释,或者如果范围(我们将在以后讨论视图范围)不是很重要,那么任何Spring注入注释都可以工作:

@SpringView
public class Add extends Composite implements View {

    @PostConstruct
    public void init() {
        setCompositionRoot(new Label("I am a view"));
    }
}

我们在这里使用了带有@PostConstructinit()方法,以确保Spring已完成注入任何字段(如果有的话)。 如果没有注入的字段,也可以使用构造函数。

Vaadin是一个完全成熟的框架,具有开发人员可以选择的多种组件 (布局,图表,网格..)。 它还提供了创建自定义组件的可能性。 例如,我们要创建一个汽车收集应用程序,以允许输入和列出汽车模型:

添加视图:

@SpringView
public class Add extends Composite implements View {

    @Autowired
    CarRepository repository;

    @Autowired
    DataProvider dataProvider;

    @PostConstruct
    public void init() {
        FormLayout formLayout = new FormLayout();
        Label title = new Label("Add new Car");
        TextField brandInput = new TextField("Brand: ");
        TextField modelInput = new TextField("Model: ");
        TextField pictureLinkInput = new TextField("Picture Link: ");
        Button button = new Button("Add", clickEvent -> {
            repository.save(new Car(brandInput.getValue(), modelInput.getValue(), pictureLinkInput.getValue()));
            Notification.show("saved");
        });
        formLayout.addComponent(title);
        formLayout.addComponent(brandInput);
        formLayout.addComponent(modelInput);
        formLayout.addComponent(pictureLinkInput);
        formLayout.addComponent(button);
        setCompositionRoot(formLayout);
    }
}

列表视图:

@SpringView
public class List extends Composite implements View {

    @Autowired
    CarRepository repository;

    @Autowired
    DataProvider dataProvider;

    @PostConstruct
    public void init() {
        Grid<Car> carGrid = new Grid<>();
        carGrid.setWidth("100%");
        carGrid.setHeight("100%");
        carGrid.setDataProvider(dataProvider);
        carGrid.addColumn(Car::getId).setCaption("Id");
        carGrid.addColumn(Car::getBrand).setCaption("Brand");
        carGrid.addColumn(Car::getModel).setCaption("Model");
        carGrid.addColumn((ValueProvider<Car, Object>) car -> 
        new ExternalResource(car.getPictureLink())).setCaption("Picture")
        .setRenderer(new ImageRenderer()).setResizable(true);
        setCompositionRoot(carGrid);
        setSizeFull();
    }
}

主界面:

@SpringUI(path="app")
@StyleSheet({"http://localhost:8080/styles.css"})
public class Main extends UI {

    @Autowired
    Add addView;

    @Autowired
    List listView;


    @Override
    protected void init(VaadinRequest vaadinRequest) {
        HorizontalLayout rootLayout = new HorizontalLayout();
        rootLayout.setSizeFull();
        HorizontalLayout mainarea = new HorizontalLayout();
        mainarea.setWidth("80%");
        Navigator navigator = new Navigator(this, mainarea);
        navigator.addView("", addView);
        navigator.addView("add", addView);
        navigator.addView("list", listView);


        CssLayout sideNav = new CssLayout();
        sideNav.setSizeFull();
        sideNav.addStyleName("sidenav");
        sideNav.setId("sideNav");
        sideNav.setWidth("20%");

        Button link1 = new Button("Add", e -> navigator.navigateTo("add"));
        link1.addStyleNames(BUTTON_LINK, MENU_ITEM);
        Button link2 = new Button("List", e -> navigator.navigateTo("list"));
        link2.addStyleNames(BUTTON_LINK, MENU_ITEM);
        sideNav.addComponent(link1);
        sideNav.addComponent(link2);
        rootLayout.addComponent(sideNav);
        rootLayout.addComponent(mainarea);
        setContent(rootLayout);
    }
}

我们创建了两个视图:一个用于添加汽车的表单和一个用于显示汽车的网格。 UI类使用navigator将两个视图连接起来。 用户界面由两部分组成:侧面导航栏(带有指向视图的链接)和主要区域(可变部分)。 我们已经将navigator配置为仅在主区域中调度视图,并配置了前往每个视图的路由:

Navigator navigator = new Navigator(this, mainarea);
        navigator.addView("", addView);
        navigator.addView("add", addView);
        navigator.addView("list", listView);

拥有默认的""空路由很重要,因为通常在启动时未设置该路由。 由于Vaadin使用自己的主题和样式表,因此@StyleSheet批注会很方便地引入自定义样式。 我们的视图和UI已连接到Spring容器,因此可以注入任何Spring bean。 例如,我们注入了CarRepository ,它是一个JpaRepository用于对Car实体执行数据库操作。

安全

Vaadin使用自己的CSRF令牌,因此,如果使用Spring Security,则应禁用Spring CSRF机制以使应用程序正常运行。 最低安全配置如下所示:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/app**").authenticated().and().formLogin().and().csrf().disable();
    }
}

包起来

Vaadin可以看作是使用Spring Boot后端快速创建UI的替代方法。 Vaadin一开始可能很容易建立,但是当复杂度增加时,学习曲线似乎并不那么平滑。 使用Vaadin(不仅使用Spring)时可能会注意到的另一个缺点是,每次进行更改时都必须重新启动整个应用程序(通常需要一段时间才能启动Spring Container),这导致需要设置HotSwap或类似的工具,无需重新启动应用程序即可热重载代码。