Autowiring services in Vaadin view and components not working - java

Hello fellow programmers! I am relatively new to Vaadin so spare me please.
I am trying to autowire my service layer into my view as follows:
#Route("")
#PWA(name = "First time bruh", shortName = "Project Base")
public class MainView extends VerticalLayout {
private TextField filterText = new TextField();
private Grid<Customer> grid = new Grid<>(Customer.class);
private CustomerForm customerForm = new CustomerForm(this);
#Autowired
private CustomerService customerService;
and the customerService dependency injections works properly, however when i try to use it in a component it returns null:
#Route
public class CustomerForm extends FormLayout {
#Autowired
private CustomerService customerService;
I've tried annotating the class with #Component and #SpringComponent but the dependency injection does not work and i think that the problem does not come from the fact that the class is not a bean, because MainView Class is also not a bean.
My wish is the custom sub components that i create to have access to the service layer.
Thanks in advance for your help!

In Vaadin UI, you can inject only in route endpoints (views that have the #Route annotation), and only if that view is opened by navigating to the route specified in the annotation. (Because only then then instantiation of that view is done "automatically").
As a rule of thumb: Whenever you instantiate something yourself using the new keyword, injection/autowiring does not work.
What I understand of your situation is:
You have a MainView, within that you want to add a CustomerForm.
Here is how to achieve that:
Inject the CustomerService into the MainView, and pass the CustomerService instance into the constructor of CustomerForm
#Route
public class MainView extends VerticalLayout {
public MainView(CustomerService customerService) { // customerService will be injected
CustomerForm customerForm = new CustomerForm(customerService);
add(customerForm);
}
}
public class CustomerForm extends FormLayout {
public CustomerForm (CustomerService customerService){
...
}
}
Another approach would be to make the CustomerForm a#Component (do remember to scan it properly in the spring configuration class), inject the service into it, and then inject the whole form into the MainView:
#Route
public class MainView extends VerticalLayout {
public MainView(CustomerForm customerForm) { // customerForm will be injected
add(customerForm);
}
}
#Component
public class CustomerForm extends FormLayout {
public CustomerForm (CustomerService customerService){ // customerService will be injected
...
}
}

Injection doesn't work when you create the instances yourself, using the new keyword.
If the class is annotated with #Route, dependencies will be injected by the Vaadin framework when navigating to that route.
In your case, if you're not navigating to it, you will have to make it a Spring bean. Add #Component or #SpringComponent (alias for the first), and either autowire it in your MainView, or autowire the ApplicationContext and get it from there.
It's fine to have both #Route and #Component, so that it can be autowired, but also navigated to.
Remember to choose an appropriate scope if you add the #Component annotation, either #UIScope to reuse the same instance while navigating around, or Scope.PROTOTYPE to always create a new one.

Related

How to inject a private field in a Component class while keep initiating with #Autowired in parent class in Spring?

I am learning Spring while I like the idea of using #Component and #Autowired to let Spring manage the dependent bean. For example, I have a Service class and Builder Class I can do with
// SomeService.java
#Service
public class SomeService {
// SomeBuilder is a #Component class
#Autowired
SomeBuilder someBuilder;
}
// SomeController.java
#Component
public class SomeController {
#Autowired
SomeService someSerivce;
}
Spring would take care of the creation of from SomeController to SomeService to SomeBuilder with the usage of #Autowired. However, now my SomeService class needs a private field which is NOT a Component class, just a plain context object, for example
// SomeService.java
#Service
public class SomeService {
#Autowired
SomeBuilder someBuilder;
private SomeContext someContext;
// Plan A: Using constructor to initiate the private field. However, I cannot use #Autowired to initiate SomeService in SomeController anymore as it requires a parameterless constructor
// Plan B: using #Autowired on constructor level, I cannot use this because SomeContext is not a #Component class
//public SomeService(SomeContext someContext) {
//this.someContext = someContext;
//}
// Plan C: This seems work but I kinda feel it's not the right way, as usually private field are initiated through constructor
//public void init(SomeContext someContext) {
// this.someContext = someContext;
//}
// demo usage of someContext
public someAnswer realMethod() {
System.out.println(someContext.getName());
}
}
Now I have no idea how to inject the someContext now, I used
plan A: Assign the private field using class constructor
plan B: Using #Autowired on constructor level
plan C: Using a wired method to assign the private field.
but I am not satisfied and don't have a clear way of doing the right approach.
First lets take a look at your plans and bust some myths/misunderstandings.
Plan A: Using constructor to initiate the private field. However, I cannot use #Autowired to initiate SomeService in SomeController anymore as it requires a parameterless constructor
Great plan, and the way to go. #Autowired doesn't depend on having a default constructor. It only indicates that you want the field to be injected with an object of that type. How that object comes to live (default constructor, constructor with arguments) doesn't matter for #Autowired. So that part of your understanding is just wrong.
using #Autowired on constructor level, I cannot use this because SomeContext is not a #Component class
If there is just a single constructor in a bean Spring will automatically use that to satisfy the dependencies. So in this case you don't need #Autowired. A bean doesn't have to be an #Component, a bean is just an instance of a class available to the application context. One way of achieving that is by marking it as an #Component but there are other ways as well. Like defining an #Bean method in in an #Configuration class to construct the bean.
#Configuration
#ComponentScan("your.base.package")
public class YourConfiguration {
#Bean
public SomeContext someContext() {
return new SomeContext();
}
}
Something along those lines. It will detect the #Component annotated classes through the #ComponentScan and will create a bean of type SomeContext for use as a bean.
Plan C: This seems work but I kinda feel it's not the right way, as usually private field are initiated through constructor
All your fields should be private not just the ones initialized in a constructor, so also the #Autowired ones. You don't want those fields to be, easily, accessible from the outside so they can be modified. So make them private.
That all being said, go with constructor injection over field injection or setters/methods for injection. It is clearer and less hidden than field injection and the way to go for mandatory dependencies (for optional dependencies you can use a setter/method).
So using the above config and below classes, it should "just work (tm)".
// SomeService.java
#Service
public class SomeService {
// SomeBuilder is a #Component class
private final SomeBuilder someBuilder;
private final SomeContext someContext;
public SomeService(SomeBuilder someBuilder, SomeContext someContext) {
this.someBuilder=someBuilder;
this.someContext=someContext;
}
}
// SomeController.java
#Component
public class SomeController {
private final SomeService someSerivce;
public SomeController(SomeService someService) {
this.someService=someService;
}
}

Spring bean decoration without ambiguity errors

I have the following scenario: A factory interface with 2 implementations, while the second one used as decorator to the first one.
public final class BaseMailFactory implements MailFactory {
#Autowired
private final ClassA classA;
#Autowired
private final ClassB classB;
public Mail createMail(){
.
.
.
}
}
public final class MetricAwareMailFactory implements MailFactory {
private final MailFactory mailFactory;
public Mail createMail(){
var mail = mailFactory.createMail();
return new MetricsAwareMail(mail);
}
}
#Configuration
public class MailFactoryConfiguration {
#Bean
public MailFactory metricsAwareMailFactory(){
return new MetricAwareMailFactory(???);
}
}
The wrapped object previously instantiated through spring container (context), hence all auto wired fields populated successfully. After creation of the second implementation I am struggle to find an elegant way to initialize the first instance without adding multiple implementations to MailFactory interface which leads to application startup errors due to ambiguity.
I know that I can use qualifies for that but they pollute my code.
I am looking for a way to instantiate a class through spring but without actually register it as a bean, in older spring versions I get to use anonymous beans for such purposes.
I found the #Primary annotation useful here:
#Configuration
public class MailFactoryConfiguration {
#Bean
#Lazy
MailFactory baseMailFactory(){
return new BaseMailFactory();
}
#Bean
#Primary
public MailFactory metricsAwareMailFactory(){
return new MetricAwareMailFactory(baseMailFactory());
}
}
I such way, both beans will be created but the primary one will be selected in case of multiple implementations.

Autowiring service in self instantiated HorizontalLayout

Is there a common way to pass an autowired service to a self instantiated view? Let's say we have a VerticalLayout managed by Spring (#Autowired works) and a HorizontalLayout a buttons inside. We need to create multiple instances of the HorizontalLayout and the button performs some action of the service (#Autowired doesn't work, Service is null). What is the best practice to solve this problem? Just pass the service as a parameter to the constructor of the HorizontalLayout is an option, but perhaps there is a better way.
#Service
public class SomeService {
public void someMethod(){
// do something
}
}
#SpringView(name = "SomeView")
#ViewScope
public class SomeView extends VerticalLayout {
#Autowired
private SomeService service;
public SomeView(){
addComponent(new Subview());
addComponent(new Subview());
}
}
public class SubView extends HorizontalLayout {
#Autowired
private SomeService service;
public SubView(){
Button btn = new Button("Test");
btn.addClickListener(e->service.someMethod());
addComponent(btn);
}
}
EDIT: I added a code example. Objects of SubView get null for service. A solution would be to pass the service to the constructor. But I wanted to know, if there is a better solution.
Do you mean a Vaadin View or just a regular Component? A View must implement the View interface (com.vaadin.navigator.View) and thus you can use the constructor to Autowire the Service and the enter() method to create your ui:
#SpringView(name = "SomeView")
#ViewScope
public class SomeView extends VerticalLayout implements View {
private SomeService service;
#Autowired
public SomeView(SomeService service){
this.service = service;
}
#Override
public void enter(ViewChangeListener.ViewChangeEvent event) {
//service is available here
removeAllComponents();
addComponent(new Subview());
addComponent(new Subview());
}
}
The way it works is that Spring will instantiate SomeView and in the process it will autowire your service through the constructor. Then, whenever the View is called it will use the enter() method to draw its components.
If it's just a regular component (I think that is your case), then instead of enter() use the #PostConstruct annotation in a method. For example:
#Autowired
public SomeView(SomeService service){
this.service = service;
}
#PostConstruct
public void init() {
//service is available here
addComponent(...);
}
I haven't tested this annotation but it's widely documented in Vaadin. Please let me know if it works for you.

Spring boot, Vaadin - cant autowire Service in a View

I have spring boot application with vaadin and vaadin4spring addon.
In one my View i want to display a data which are in database. Data i want to display are returned by a method in service class.
#Service
public class UserService {
public List<Users> getUsers() ....
}
View:
#SpringView(name = Views.USERLIST) //static constant containing string "users"
public class UserlistView extends HorizontalLayout implements View {
#Autowired
private UserService userService;
#PostConstruct
public void construct() {
displaytable(userService.getUsers());
}
#Override
public void enter(ViewChangeListener.ViewChangeEvent viewChangeEvent) {}
}
UI class:
#SpringUI
#Theme("valo")
public class TUI extends UI {
#Autowired
private SpringViewProvider springViewProvider;
protected void init(VaadinRequest request) {
..
Navigator navigator = new Navigator(this, content);
navigator.addView(Views.USERLIST, UserlistView.class);
navigator.addProvider(springViewProvider);
}
}
During the server startup vaadin recognizes the springview - " c.v.spring.navigator.SpringViewProvider : 1 SpringView found", no exceptions or warnings are displayed.
Every time i use navigator display the view (navigator.navigateTo("users")) autowired field in the view is not injected and the postprocess method not called. What is even stranger if i dont add the view into the navigator object and use url to access the view directly - localhost:8080/#!users the fields will be injected correctly and method construct() will be called.
If it helps there is my configuration class
#Configuration
#ComponentScan(basePackages = { "me.project" })
#EnableAutoConfiguration
#EnableVaadinEventBus
public class TheConfig {
// contains only one bean definition - sessionfactory
}
Has anyone faced similar problem? I was not able to find help anywhere could anyone please tell me what i did wrong.
Thank you
When you do
navigator.addView(Views.USERLIST, UserlistView.class);
It creates a StaticViewProvider which is not handled by Spring and creates a view using 'new'. So no spring autowiring happens for 'addView'
If you use springViewProvider Autowiring is handled by Spring.
So do not do navigator.addView(), instead rely on springViewProvider when using Spring+Vaadin.

Problems with Injection (Using vaadin)

I am newbie with EJB and Injections...
I am currently using Vaadin framework with CDI
I have been trying to using injection but i have not could do it...
In my Vaadin UI class MyVaadinUI i have tried...
CDIUI("")
#SuppressWarnings("serial")
public class MyVaadinUI extends UI {
#EJB
UserController userController;
#Override
protected void init(VaadinRequest request) {
System.err.println("desde controller "+userController.getAll().size());
}
}
UserController
#Stateless
public class UserController {
#EJB
IUserDAO userDao;
public List<User> getAll() {
return userDao.findAll();
}
}
and it works!!
but when I do not inject UserController, it does not work... In other words when I instance the class UserController the injection in this class does not work...
Code does not work
CDIUI("")
#SuppressWarnings("serial")
public class MyVaadinUI extends UI {
#Override
protected void init(VaadinRequest request) {
UserController userController = new UserController();
System.err.println("desde controller "+userController.getAll().size());
}
}
Somebody can explain me why?
Thanks
Nicolas
Only in injected objects will have its dependencies injected. If you create an object with new all field having #inject, #ejb or #resource will not be injected.
In your case you create UserController like this:
UserController userController = new UserController();
and so this field will not be injected:
#EJB
IUserDAO userDao;
And therefore getAll() will throw a NullPointerException.
I use vaadin and cdi for projects. I'd recommend to use injection for almost everything or not at all. I inject my uis, views, own components... (and do not create them with new) so it is possible to inject ejb beans or other things into them. If you are using it only sometimes you are ending up with am mixture of injection and normal object creation and will have to pass around injected objects to other object you instantiated yourself. In another project of mine this happened and got really problematic for future changed in the code.

Categories

Resources