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.
Related
I am new at spring MVC framework and i am currently working in a web application that uses a session scoped bean to control some data flow.
I can access these beans in my application context using #Autowired annotation without any problem in the controllers. The problem comes when I use a class in service layer that does not have any request mapping (#RequestMapping, #GetMapping nor #PostMapping) annotation.
When I try to access the application context directly or using #Autowired or even the #Resource annotation the bean has a null value.
I have a configuration class as follow:
#Configuration
#EnableAspectJAutoProxy
#EnableJpaRepositories(repositoryFactoryBeanClass = EnversRevisionRepositoryFactoryBean.class, basePackages = "com.quantumx.nitididea.NITIDideaweb.repository")
public class AppConfig implements WebMvcConfigurer {
#Bean (name = "lastTemplate")
#SessionScope
public LastTemplate getlastTemplate() {
return new LastTemplate();
}
//Some extra code
}
The POJO class is defined as :
public class LastTemplate {
private Integer lastId;
public LastTemplate(){
}
public Integer getLastId() {
return lastId;
}
public void setLastId(Integer lastId) {
this.lastId = lastId;
}
}
The I have a Test class that is annotated as service and does not have any request mapping annotated method:
//#Controller
#Service
public class Test {
// #Autowired
// private ApplicationContext context;
// #Autowired
#Resource(name = "lastTemplate")
public LastTemplate lastTemplate;
// #Autowired
// public void setLastTemplate(LastTemplate lastTemplate) {
// this.lastTemplate = lastTemplate;
// }
public Test() {
}
// #RequestMapping("/test")
public String testing() {
// TemplateForma last = (TemplateForma) context.getBean("lastInsertedTemplate");
// System.out.println(last);
System.out.println(lastTemplate);
// System.out.println(context.containsBean("lastTemplate"));
// System.out.println(context.getBean("lastTemplate"));
System.out.println("Testing complete");
return "Exit from testing method";
// return "/Messages/Success";
}
}
As you can see, there is a lot of commented code to show all the ways i have been trying to access my application context, using an Application context dependency, autowiring, declaring a resource and trying with a request mapping. The bean is null if no controller annotation and request mapping method is used and throws a java null pointer exception when I use the context getBean() methods.
Finally I just test my class in a controller that i have in my app:
#RequestMapping("/all")
public String showAll(Model model) {
Test test = new Test();
test.testing();
return "/Administrator/test";
}
Worth to mention that I also tried to change the scope of the bean to a Application scope and singleton, but it not worked. How can access my application context in a service class without mapping a request via controller?
Worth to mention that I also tried to change the scope of the bean to a Application scope and singleton, but it not worked
It should have worked in this case.
How can access my application context in a service class without mapping a request via controller?
Try one of these :-
#Autowired private ApplicationContext appContext;
OR
Implement ApplicationContextAware interface in the class where you want to access it.
Edit:
If you still want to access ApplicationContext from non spring managed class. Here is the link to article which shows how it can be achieved.
This page gives an example to get spring application context object with in non spring managed classes as well
What worked for me is that session scoped bean had to be removed in the application configuration declaration and moved to the POJO definition as follows:
#Component
#SessionScope
public class LastTemplate {
private Integer lastId;
public LastTemplate(){
}
public Integer getLastId() {
return lastId;
}
public void setLastId(Integer lastId) {
this.lastId = lastId;
}
}
The I just call the bean using #Autowired annotation.
I've a Spring Boot application with the Spring Data JPA and two entities mapped to a database. My application is set to recreate the database on every startup. Now i want to create some instances of these POJOs, persist them in the database so I've some data to test my application with in the ongoing development process.
What is the best way to do that?
What i tried so far:
My class to add the sample data
public class DBSampleAdditor {
#PersistenceContext
private EntityManager em;
#Transactional
public void addSampleData() {
// creating POJO instances and persist them with em.perists()
}
}
I tried to call these functions in the main of my ApplicationClass
#SpringBootApplication()
public class ApiApplication {
public static void main(String[] args) {
SpringApplication.run(ApiApplication.class, args);
DBSampleAdditor dbsa = new DBSampleAdditor();
dbsa.addSampleData();
}
}
That didnt work at all, as the EntityManager never got an Instance from the persistence unit.
I also tried to create a CommandLineRunner:
#Component
public class PTCommandLineRunner implements CommandLineRunner {
#Override
public void run(String... args) throws Exception {
System.out.println("Adding sample data...");
DBSampleAdditor dbsa = new DBSampleAdditor();
dbsa.addSampleData();
}
}
But that one seemed to never been called in the startup process.
You can use method with #PostConstruct with #Component to insert data at startup.
#PostConstruct will be invoked after the bean has been created, dependencies have been injected, all managed properties are set, and before the bean is actually set into scope.
Just inject your database repository in the class marked with #Component and call your injected database repository inside the method marked with #PostConstruct
Example:
#Component
public class DbInit {
#Autowired
private UserRepository userRepository;
#PostConstruct
private void postConstruct() {
User admin = new User("admin", "admin password");
User normalUser = new User("user", "user password");
userRepository.save(admin, normalUser);
}
}
In order to use spring managed components like the EntityManager the objects need to spring Beans, which basically means they are created inside the spring context and can utilise the IoC container.
In the example above, running the DbSampleAdditor class from the main method is outside the spring context, so the beans like PersistenceContext wont get injected.
One fairly simple way to do this inside the spring context is to add an ApplicationListener for the ApplicationReadyEvent
#Component
class Initialise implements ApplicationListener<ApplicationReadyEvent> {
#Autowired
private final DbSampleAdditor db;
#Override
public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
db.addSampleData();
}
}
When the application ready event fires all the necessary spring bean wiring is set up so when it runs the addSampleData method, things like the EM are good to go.
Other approaches for setting up a DB for spring boot are documented here
Instead of creating an object DBSampleAdditor, try to autowire it so that it is available on application startup. #Autowired DBSampleAdditor DBSampleAdditor Then you try to add sample data within the run method
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.
In my Spring Boot application, i have a configuration, which reads entries from a Mongo database.
After this is done, my subclass of AbstractMongoEventListener is created, even though it operates on a different table and different scope (my own custom #CustomerScope).
Here is the listener:
#CustomerScoped
#Component
public class ProjectsRepositoryListener extends AbstractMongoEventListener<Project> {
#Override
public void onAfterSave(Project source, DBObject dbo) {
System.out.println("saved");
}
}
And here the configuration:
#Configuration
public class MyConfig {
#Autowired
private CustomersRepository customers;
#PostConstruct
public void initializeCustomers() {
for (Customer customer : customers.findAll()) {
System.out.println(customer.getName());
}
}
}
I find it surprising that the listener is instantiated at all. Especially since it is instantiated well after the call to the customers repository has finished.
Is there a way to prevent this? I was thinking of programmatically registering it per table/scope, without annotation magic.
To prevent auto-instantiation, the listener must not be annotated as #Component. The configuration needs to get ahold of the ApplicationContext, which can be autowired.
Thus, my configuration class looks like this:
#Autowired
private AbstractApplicationContext context;
private void registerListeners() {
ProjectsRepositoryListener firstListener = beanFactory.createBean(ProjectsRepositoryListener.class);
context.addApplicationListener(firstListener);
MySecondListener secondListener = beanFactory.createBean(MySecondListener.class);
context.addApplicationListener(secondListener);
}
Note that this works for any ApplicationListener, not just AbstractMongoEventListener.
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.