KEMBAR78
Vaadin DevDay 2017 - DI your UI | PDF
5
S T O R Y A N D P H I L O S O P H Y
Software is eating the world and what most of us see of it is the user interface. The user
interface has become the key component of how the users experience the business
behind it. Competition is lost or won due to user experience. Simplicity is king and the
users get frustrated by anything ugly, slow or not working on the device they happen to
use at the time. We at Vaadin fight for simplicity and invite everyone to join this fight.
Together we want to build a user interface that puts a smile on the user’s face.
Vaadin is the technology that empowers developers to build the best web-apps for
business purposes. Our priority over everything else is developer productivity because
we believe that by simplifying the developer experience and saving the developer’s
time, they are best able to focus on building great user interfaces.
Our brand is what we want everyone to think about us. When everyone - both us and
the people around us - have a consistent understanding of what Vaadin is and what we
stand for, it enables that image to spread and amplify. This book defines what we want
that image to be. It defines what the Vaadin brand is.
I hope that You are as excited and proud of living and breathing the Vaadin brand as
I am. You are the one who is shaping what everyone thinks about Vaadin - using this
brand as a tool and a guideline every day.
Let’s fight for simplicity for both the users and the developers!
Joonas Lehtinen
Founder & CEO
Vaadin
I N T R O D U C T I O N
@peter_lehto
D I Y O U R U I
Session’s content
Session’s content
• What Dependency Injection, Why and How?
Session’s content
• What Dependency Injection, Why and How?
• Vaadin UI, View and components as Beans
Session’s content
• What Dependency Injection, Why and How?
• Vaadin UI, View and components as Beans
• Instances and Scopes
Session’s content
• What Dependency Injection, Why and How?
• Vaadin UI, View and components as Beans
• Instances and Scopes
• EventBus and other DI Extensions
Session’s content
• What Dependency Injection, Why and How?
• Vaadin UI, View and components as Beans
• Instances and Scopes
• EventBus and other DI Extensions
• Tips and Tricks for Springifying your Vaadin app
Session’s content
• What Dependency Injection, Why and How?
• Vaadin UI, View and components as Beans
• Instances and Scopes
• EventBus and other DI Extensions
• Tips and Tricks for Springifying your Vaadin app
W h a t D e p e n d e n c y I n j e c t i o n ?
WHAT DI? private Grid<Customer> grid;
INSTEAD OF
NEW private Grid<Customer> grid;
protected void init(VaadinRequest request) {
grid = new Grid<>();
grid.setSizeFull();
SAY
@AUTOWIRED
@Autowired
private Grid<Customer> grid;
protected void init(VaadinRequest request) {
grid.setSizeFull();
@Bean
public Grid<Customer> configureBeanGrid() {
BEAN
CONFIG
@Bean
public Grid<Customer> configureBeanGrid() {
Grid<Customer> grid = new Grid<>();
BEAN
CONFIG
@Bean
public Grid<Customer> configureBeanGrid() {
Grid<Customer> grid = new Grid<>();
grid.addColumn(Customer::getFirstName).setCaption("First name");
grid.addColumn(Customer::getLastName).setCaption("Last name");
BEAN
CONFIG
@Bean
public Grid<Customer> configureBeanGrid() {
Grid<Customer> grid = new Grid<>();
grid.addColumn(Customer::getFirstName).setCaption("First name");
grid.addColumn(Customer::getLastName).setCaption("Last name");
return grid;
}
BEAN
CONFIG
Dependency Injection (DI) is a runtime mechanism
Dependency Injection (DI) is a runtime mechanism
where dependency between the client object and the
dependent object does not occur directly.
Dependency Injection (DI) is a runtime mechanism
where dependency between the client object and the
dependent object does not occur directly.
With DI the client object does not necessarily
manage the lifecycle of the dependent object.
Dependency Injection (DI) is a runtime mechanism
where dependency between the client object and the
dependent object does not occur directly.
With DI the client object does not necessarily
manage the lifecycle of the dependent object.
Instead with DI a special DI container takes care of
the object lifecycle management
Dependency Injection (DI) is a runtime mechanism
where dependency between the client object and the
dependent object does not occur directly.
With DI the client object does not necessarily
manage the lifecycle of the dependent object.
Instead with DI a special DI container takes care of
the object lifecycle management where clients
reference managed and possibly shared objects.
W h y ?
•
High Abstraction
•
High Abstraction
•
Loose coupling
•
High Abstraction
•
Loose coupling
•
Dependency inversion
•
High Abstraction
•
Loose coupling
•
Dependency inversion
•
Highly cohesive modules
•
High Abstraction
•
Loose coupling
•
Dependency inversion
•
Highly cohesive modules
•
Deployment time config
H o w ?
HIGH
ABSTRACTION
interface GadgetConfiguration
HIGH
ABSTRACTION
interface GadgetConfiguration
interface Gadget<C extends GadgetConfiguration>
HIGH
ABSTRACTION
interface GadgetConfiguration
interface Gadget<C extends GadgetConfiguration>
interface ConfigurationDialog<C extends GadgetConfiguration>
HIGH
ABSTRACTION
class NotesConfiguration implements GadgetConfiguration {
}
HIGH
ABSTRACTION
class NotesConfiguration implements GadgetConfiguration {
}
class NotesGadget implements Gadget<NotesConfiguration> {
}
HIGH
ABSTRACTION
class NotesConfiguration implements GadgetConfiguration {
}
class NotesGadget implements Gadget<NotesConfiguration> {
}
class NotesConfigurationDialog implements
ConfigurationDialog<NotesConfiguration> {
}
HIGH
ABSTRACTION
class NotesConfiguration implements GadgetConfiguration {
}
@Component
class NotesGadget implements Gadget<NotesConfiguration> {
}
@Component
class NotesConfigurationDialog implements
ConfigurationDialog<NotesConfiguration> {
}
APPLICATION
CONTEXT
String[] getBeanNamesForType(Class<?> type);
String[] getBeanNamesForType(ResolvableType type);
Map<String, T> getBeansOfType(Class<T> type);
Map<String, Object> getBeansWithAnnotation(Class<?> annot);
Object getBean(String);
T getBean(String, Class<T> type);
LOOSE
COUPLING interface MainMenu { }
• Define abstraction
LOOSE
COUPLING interface MainMenu { }
@Component
class DefaultMainMenu implements MainMenu {
}
• Define abstraction
• Define Implementations
LOOSE
COUPLING interface MainMenu { }
@Component
class DefaultMainMenu implements MainMenu {
}
@Component
class ResponsiveMainMenu implements MainMenu {
}
• Define abstraction
• Define Implementations
DEPENDENCY
INVERSION
@Autowired
private MainMenu mainMenu;
• Define abstraction
• Define Implementations
• Depend on Abstraction,

not concrete type -> 

Depency Inversion
@Component
public class DefaultMainMenu implements MainMenu { … }
D e f i n i n g a B e a n
@Component
@UIScope
public class DefaultMainMenu implements MainMenu { … }
D e f i n i n g a B e a n
@Configuration
public class ComponentConfiguration {
@Bean
@Primary
@UIScope
public MainMenu provideDefaultMenu {
return new DefaultMainMenu();
}
… w i t h @ C o n f i g u r a t i o n
@Bean
@Responsive
@UIScope

public MainMenu provideResponsiveMenu {
return new ResponsiveMainMenu();
}
}
Session’s content
• What Dependency Injection, Why and How?
• Vaadin UI, View and components as Beans
• Instances and Scopes
• EventBus and other DI Extensions
• Tips and Tricks for Springifying your Vaadin app
Automatic discovery
and lookup
UI AS BEAN
@SpringUI
public class VaadinUI extends UI {
path attribute for
URL binding
UI AS BEAN
@SpringUI(path = "app")
public class VaadinUI extends UI {
@SpringUI
public class VaadinUI extends UI {
localhost:8080/context
UI AS BEAN
localhost:8080/context/app
@SpringUI(path = "app")
public class VaadinUI extends UI {
@SpringUI
public class VaadinUI extends UI {
HorizontalLayoutContentAreaMenu
View1
View2
View3
HorizontalLayoutView1Menu
View1
View2
View3
HorizontalLayoutView2Menu
View1
View2
View3
HorizontalLayoutView3Menu
View1
View2
View3
Implement View and
annotate with
@SpringView
VIEW AS BEAN
@SpringView(name = "customers")
public class CustomerView extends VerticalLayout
implements View {
Wrapper for View
Component in UI
VIEWDISPLAY
@SpringViewDisplay
public class DevDayViewDisplay
extends VerticalSplitPanel
implements ViewDisplay {
S p r i n g V i e w P r o v i d e r
SPRING NAVIGATOR
@Autowired
private Navigator navigator;
navigator.navigateTo(“customers”);
N a v i g a t o r
V i e w b e a n d i s c o v e r y b y @ S p r i n g V i e w
SPRING NAVIGATOR
public interface ViewAccessControl;
public interface ViewInstanceAccessControl;
C o n t r o l l i n g a c c e s s t o v i e w s
COMPONENTS
AS
BEANS
@Autowired
private Grid<Customer> grid;
DEMO
Session’s content
• What Dependency Injection, Why and How?
• Vaadin UI, View and components as Beans
• Instances and Scopes
• EventBus and other DI Extensions
• Tips and Tricks for Springifying your Vaadin app
H o w a r e t h e b e a n
i n s t a n c e s m a n a g e d ?
WITH SCOPES
@ S e s s i o n S c o p e
WITH SCOPES
@Autowired
private User currentUser;
@ S e s s i o n S c o p e
@ S e s s i o n S c o p e
@ Va a d i n S e s s i o n S c o p e
WITH SCOPES
@ S e s s i o n S c o p e
@ Va a d i n S e s s i o n S c o p e
@ U I S c o p e
WITH SCOPES
public interface MainMenu { … }
@Autowired
private MainMenu mainMenu;
@Component
@UIScope
public class DefaultMainMenu implements MainMenu { … }
@ U I S c o p e
@ S e s s i o n S c o p e
@ Va a d i n S e s s i o n S c o p e
@ U I S c o p e
@ V i e w S c o p e
WITH SCOPES
@Component
@ViewScope
public class NotesGadget {
@Autowired
private EventBus.ViewEventBus eventBus;
@ V i e w S c o p e
@ S e s s i o n S c o p e
@ Va a d i n S e s s i o n S c o p e
@ U I S c o p e
@ V i e w S c o p e
@ R e q u e s t S c o p e
WITH SCOPES
public interface HttpRequestStopWatch { … }
@ R e q u e s t S c o p e
@SessionScope
@SessionScope
@VaadinSessionScope
@SessionScope
@VaadinSessionScope
@UIScope
@SessionScope
@VaadinSessionScope
@UIScope
@ViewScope
@SessionScope
@ApplicationScoped@SessionScope
@Vaadin
SessionScope
@Vaadin
SessionScope
@Vaadin
SessionScope
@SessionScope
@Vaadin
SessionScope
@Vaadin
SessionScope
@Vaadin
SessionScope
@UI
Scope
@UI
Scope
@UI
Scope
@UI
Scope
@UI
Scope
@UI
Scope
@
View
@
View
@
View
@
View
@
View
@
View
@
View
@
View
@
View
@
View
@
View
@
View
@UI
Scope
@UI
Scope
@UI
Scope
@UI
Scope
@UI
Scope
@UI
Scope
@SessionScope
@Vaadin
SessionScope
@Vaadin
SessionScope
@Vaadin
SessionScope
Session’s content
• What Dependency Injection, Why and How?
• Vaadin UI, View and components as Beans
• Instances and Scopes
• EventBus and other DI Extensions
• Tips and Tricks for Springifying your Vaadin app
E v e n t B u s
@Component
@ViewScope
public class DataEditor<DTO> {
}
E v e n t B u s
@Component
@ViewScope
public class DataEditor<DTO> {
@Autowired
private EventBus.ViewEventBus eventBus;
…
}
E v e n t B u s
@Component
@ViewScope
public class DataEditor<DTO> {
@Autowired
private EventBus.ViewEventBus eventBus;
protected void onSaveClicked() {
eventBus.publish(this, new EditorSaveEvent());
}
…
}
@Component
@ViewScope
public class DataTable<DTO> extends Grid<DTO> {
@Autowired
private EventBus.ViewEventBus eventBus;
}
@Component
@ViewScope
public class DataTable<DTO> extends Grid<DTO> {
@Autowired
private EventBus.ViewEventBus eventBus;
@PostConstruct
protected void initialize() {
eventBus.subscribe(this);
}
}
@Component
@ViewScope
public class DataTable<DTO> extends Grid<DTO> {
@Autowired
private EventBus.ViewEventBus eventBus;
@PostConstruct
protected void initialize() {
eventBus.subscribe(this);
}
@EventBusListenerMethod
protected void onSaveEvent(EditorSaveEvent e) {
getDataProvider().refreshAll();
}
}
Va a d i n I 1 8 N S u p p o r t
@EnableI18N
Va a d i n I 1 8 N S u p p o r t
@EnableI18N
@Bean
I18N i18n() {
return new I18N(context);
}
Va a d i n I 1 8 N S u p p o r t
@EnableI18N
@Bean
I18N i18n() {
return new I18N(context);
}
@Bean
CompositeMessageSource messageSource() {
return new CompositeMessageSource(context);
}
Va a d i n I 1 8 N S u p p o r t
@Bean
MessageProvider provideTranslations() {
return new ResourceBundleMessageProvider
(“com.foo.path.to.bundle”, "UTF-8");
}
Session’s content
• What Dependency Injection, Why and How?
• Vaadin UI, View and components as Beans
• Instances and Scopes
• EventBus and other DI Extensions
• Tips and Tricks for Springifying your Vaadin app
S e t t i n g u p m e n u a u t o m a t i c a l l y
@MenuDefinition(icon=, name=, order=)
@SpringView(name=“customers”)
public class CustomerViewBean implements View… {
…
}
S e t t i n g u p m e n u a u t o m a t i c a l l y
private void findAndPopulateMenuItems() {
List<String> beanNames = Arrays.asList(context.
getBeanNamesForAnnotation(MenuDefinition.class));
}
S e t t i n g u p m e n u a u t o m a t i c a l l y
private void findAndPopulateMenuItems() {
List<String> beanNames = Arrays.asList(context.
getBeanNamesForAnnotation(MenuDefinition.class));
Map<String, MenuDefinition> definitionsToNames = beanNames.stream().
collect(Collectors.toMap(Function.identity(),
beanName -> context.findAnnotationOnBean(beanName, MenuDefinition.class)));
Map<String, SpringView> viewsToNames = beanNames.stream().
collect(Collectors.toMap(Function.identity(),
beanName -> context.findAnnotationOnBean(beanName, SpringView.class)));
}
S e t t i n g u p m e n u a u t o m a t i c a l l y
private void findAndPopulateMenuItems() {
List<String> beanNames = Arrays.asList(context.
getBeanNamesForAnnotation(MenuDefinition.class));
Map<String, MenuDefinition> definitionsToNames = beanNames.stream().
collect(Collectors.toMap(Function.identity(),
beanName -> context.findAnnotationOnBean(beanName, MenuDefinition.class)));
Map<String, SpringView> viewsToNames = beanNames.stream().
collect(Collectors.toMap(Function.identity(),
beanName -> context.findAnnotationOnBean(beanName, SpringView.class)));
beanNames.forEach(beanName -> {
MenuDefinition menuDefinition = definitionsToNames.get(beanName);
SpringView viewDefinition = viewsToNames.get(beanName);
addMenuItem(menuDefinition.name(), menuDefinition.icon(),
viewDefinition.name());
});
Lessons learned
Lessons learned
• DI is a powerful mechanism to decouple code
Lessons learned
• DI is a powerful mechanism to decouple code
• Following DI almost certainly guarantees that best practices
are followed
Lessons learned
• DI is a powerful mechanism to decouple code
• Following DI almost certainly guarantees that best practices
are followed
• Vaadin supports DI with Spring and CDI, both through their
own integration add-ons
Lessons learned
• DI is a powerful mechanism to decouple code
• Following DI almost certainly guarantees that best practices
are followed
• Vaadin supports DI with Spring and CDI, both through their
own integration add-ons
• Lot of Spring functionality is based on Beans
Lessons learned
• DI is a powerful mechanism to decouple code
• Following DI almost certainly guarantees that best practices
are followed
• Vaadin supports DI with Spring and CDI, both through their
own integration add-ons
• Lot of Spring functionality is based on Beans
• Structuring Vaadin app with Bean approach can provide
great flexibility and robustness
@peter_lehto
T H A N K Y O U !

Vaadin DevDay 2017 - DI your UI