如何使用Vaadin + Spring创建MVP架构?

时间:2014-05-12 15:26:00

标签: spring vaadin mvp spring-boot vaadin4spring

我想创建以下简单的MVP架构:

  • 查看类似于vaadin布局,组件和样式的类。非功能性。视图应与当前的ViewScope / SessionScope绑定,因此我使用https://github.com/peholmst/vaadin4spring@UIScope

  • 演示者应该注入视图,在视图组件上注册侦听器,处理用户输入并委托模型服务

问题:当我将视图注入演示者时,视图会重新创建,因此演示者和视图不在同一范围内。所以绑定不起作用。 我可以改变什么来实现上述设计?

@VaadinComponent
@UIScope
public class LoginView {
    //form fields, buttons
}

@Controller
public class LoginPresenter implements ClickListener {
    @Autowired
    private LoginView view;

    @PostConstruct
    public void bind() {
        view.getLoginButton().addClickListener(this);
    }   

    @Override
    public void buttonClick(ClickEvent event) {
        //validate input and login
    }   
}

2 个答案:

答案 0 :(得分:5)

也许像

public class LoginView {

    @Autowired
    public void initPresenter(LoginPresenter loginPresenter) {
        loginPresenter.setLoginView(this);
        loginPresenter.bind();
    }
}

@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class LoginPresenter {

    private LoginView loginView;

    public void bind() {
        // ...
    }

    public LoginView getLoginView() {
        return loginView;
    }

    public void setLoginView(LoginView loginView) {
        this.loginView = loginView;
    }

}

修改

您可以解耦添加配置界面,但会增加一些复杂性,例如

    public interface View {

    }

    public interface Presenter {

        void setView(View view);
        void bind();
    }

    public interface ViewManager {

        void configure(View view);
    }

    public class ViewSupport implements View {

        @Autowired
        private ViewManager viewManager;

        @PostConstruct
        public void init() {
            viewManager.configure(this);
        }
    }



 /**
  * ViewManager that configure Presenters following 
  * the naming convention XXView->XXPresenter
  */  
 public class DefaultViewManager implements ViewManager {


    @Autowired
    private ApplicationContext applicationContext;

    @Override
    public void configure(View view) {
        Presenter p = (Presenter) applicationContext.getBean(getPresenterName(view.getClass()));
        p.setView(view);
        p.bind();

    }

    protected String getPresenterName(Class<?> clazz) {
        return StringUtils.uncapitalize(clazz.getSimpleName()).replace("View", "Presenter");
    }

}

答案 1 :(得分:1)

我在当前的项目中遇到了类似的问题,我现在以下列方式解决了这个问题:

@VaadinUI
public class MainUI extends UI {

    @Autowired
    private MainView mainView;

    @Autowired
    private MainPresenter mainPresenter;

    @Autowired
    private Test1Presenter test1Presenter;

    @Autowired
    private Test2Presenter test2Presenter;

    @Override
    protected void init(final VaadinRequest vaadinRequest) {

        setContent(this.mainView);
    }
}

@Component
@Scope("ui")
@VaadinView(name = "MainView")
public class MainView extends CustomComponent {

    private static final Logger LOGGER = LoggerFactory.getLogger(MainView.class);

    private TabSheet tabSheet;

    @Autowired
    private Test1Tab test1Tab;

    @Autowired
    private Test2Tab test2Tab;

    @PostConstruct
    private void init() {

        LOGGER.debug("MainView - Method init - Test1Tab: " + this.test1Tab);

        this.tabSheet = new TabSheet();
        this.tabSheet.addTab(this.test1Tab);
        this.tabSheet.addTab(this.test2Tab);

        setCompositionRoot(this.tabSheet);
    }

    public TabSheet getTabSheet() {

        return this.tabSheet;
    }

    public Test1Tab getTest1Tab() {

        return this.test1Tab;
    }

    public Test2Tab getTest2Tab() {

        return this.test2Tab;
    }
}

@Controller
@Scope("ui")
// Implementing Serializable was necessary to remove the warning "Storing
// non-serializable bean [...] with name [...] in UI space [...]".
public class MainPresenter implements Serializable {

    @Autowired
    private MainModel model;

    @Autowired
    private MainView view;

    @PostConstruct
    private void init() {

        // ...
    }
}

@Component
@Scope("ui")
@VaadinView(name = "Test1Tab")
public class AlarmsTab extends VerticalLayout {

    private final Button test1Button = new Button("Test 1");

    private final Button test2Button = new Button("Test 2");

    public Test1Tab() {

        setCaption("Test1");
        setMargin(true);
        setSpacing(true);

        addComponent(this.test1Button);
        addComponent(this.test2Button);
    }

    public Button getTest1Button() {

        return this.test1Button;
    }

    public Button getTest2Button() {

        return this.test2Button;
    }
}

@Controller
@Scope("ui")
public class Test1Presenter implements Serializable {

    private static final Logger LOGGER = LoggerFactory.getLogger(Test1Presenter.class);

    @Autowired
    private MainModel model;

    @Autowired
    private Test1Tab view;

    @PostConstruct
    private void init() {

        LOGGER.debug("Test1Presenter - Method init - Test1Tab: " + this.view);

        this.view.getTest1Button().addClickListener(new ClickListener() {

            @Override
            public void buttonClick(ClickEvent event) {

               // ... 
            }
        });

        this.view.getTest2Button().addClickListener(new ClickListener() {

            @Override
            public void buttonClick(ClickEvent event) {

               // ... 
            }
        });
    }
}

这种方法有效,但我不确定我是否正确使用了范围(视图和演示者都使用@Scope(“ui”)注释)。

对此有何评论?