Hi, this problem has been bugging me for a long time. What is the best practice to instantinate Vaadin components that have dependencies on Spring services and also runtime parameters? Just explaining. Let there be some Spring services
[code]
@Service
public class GreetingService {
public String getGreeting(String name) {
return "Hello " + name;
}
}
@Service
public class LuckyNumberService {
public int getLuckyNumber(String name) {
return name.hashCode() % 10;
}
}
[/code] and i would like to create Dialog window which displays data from the services for specific user.
Solution 1
Inject services to a managed UI/View and pass them to the dialog. A problem with this solution is that a parent container can end up with a large number of injected servies and every change to child dependencies causes change of the parent code.
public class SampleDialog extends Window {
public SampleDialog(String name,
GreetingService greetingService,
LuckyNumberService luckyNumberService) {
setContent(new Label(greetingService.getGreeting(name) + "Your lucky numer is " + luckyNumberService.getLuckyNumber(name)));
}
}
@SpringUI
public class MainUI extends UI {
@Autowired
private GreetingService greetingService;
@Autowired
private LuckyNumberService luckyNumberService;
@Override
protected void init(VaadinRequest request) {
// build some ui with a button and handle click with a function below
}
private void handleAction(String name){
SampleDialog dialog = new SampleDialog(name,greetingService,luckyNumberService);
addWindow(dialog);
}
Solution 2
Create managed factory with injected servies. Downside is some boilerplate code and additional class for each component type, but this is probably the cleanest solution from api user perspective.
public class SampleDialog extends Window {
public SampleDialog(String name,
GreetingService greetingService,
LuckyNumberService luckyNumberService) {
setContent(new Label(greetingService.getGreeting(name) + "Your lucky numer is " + luckyNumberService.getLuckyNumber(name)));
}
}
@Component
public class SampleDialogFactory {
@Autowired
private GreetingService greetingService;
@Autowired
private LuckyNumberService luckyNumberService;
public SampleDialog createSampleDialogForUser(String name) {
return new SampleDialog(name,greetingService,luckyNumberService);
}
}
@SpringUI
public class MainUI extends UI {
@Autowired
private SampleDialogFactory sampleDialogFactory;
@Override
protected void init(VaadinRequest request) {
// build some ui with a button and handle click with a function below
}
private void handleAction(String name) {
SampleDialog dialog = sampleDialogFactory.createSampleDialogForUser(name);
addWindow(dialog);
}
}
Solution 3
Create managed dialog prototype and initalize it with runtime parameters after construcion. Api user must remember to call initialization function. What if he doesn’t? Can component be reused? Below is a code with two approaches to instantinate dialog component
@Component
@Scope("prototype")
public class SampleDialog extends Window {
private GreetingService greetingService;
private LuckyNumberService luckyNumberService
public SampleDialog(GreetingService greetingService,
LuckyNumberService luckyNumberService) {
this.greetingService = greetingService;
this.luckyNumberService = luckyNumberService;
}
public void init(String name) {
setContent(new Label(greetingService.getGreeting(name) + "Your lucky numer is " + luckyNumberService.getLuckyNumber(name)));
}
}
@SpringUI
public class MainUI extends UI {
@Autowired
private SampleDialog dialog;
@Autowired
private ApplicationContext applicationContext;
@Override
protected void init(VaadinRequest request) {
// build some ui with a button and handle click with a function below
}
private void handleAction(String name) {
dialog.init(name);
addWindow(dialog);
}
private void alternativeHandleAction(String name) {
// alternative to injecting protype into component
SampleDialog dialog = applicationContext.getBean(SampleDialog.class);
dialog.init(name);
addWindow(dialog);
}
}
What do you think? Which one do you use? Or is there any other solution?
During writing of this post I nearly convinced myself that factory is the best solution, but still it seems like a lot of boilerplate code.