Autowiring service in self instantiated HorizontalLayout - java

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.

Related

Autowiring services in Vaadin view and components not working

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.

How to Mock an injected object that is not declared in Module?

For a dagger2 module
#Module
public class MyModule {
#Provides #Singleton public RestService provideRestService() {
return new RestService();
}
#Provides #Singleton public MyPrinter provideMyPrinter() {
return new MyPrinter();
}
}
We could have the test module as Test
public class TestModule extends MyModule {
#Override public MyPrinter provideMyPrinter() {
return Mockito.mock(MyPrinter.class);
}
#Override public RestService provideRestService() {
return Mockito.mock(RestService.class);
}
}
However if for a class as below that is not declared in the dagger module...
public class MainService {
#Inject MyPrinter myPrinter;
#Inject public MainService(RestService restService) {
this.restService = restService;
}
}
How do I create a mock of MainService as above.
Note, I'm not planning to perform test for MainService as per share in https://medium.com/#fabioCollini/android-testing-using-dagger-2-mockito-and-a-custom-junit-rule-c8487ed01b56#.9aky15kke, but instead, my MainService is used in another normal class that I wanted to test. e.g.
public class MyClassDoingSomething() {
#Inject MainService mainService;
public MyClassDoingSomething() {
//...
}
// ...
public void myPublicFunction() {
// This function uses mainService
}
}
This is definitely not answering your question, but in my honest opinion it is related, it's helpful and too big for a comment.
I'm often facing this question and I end always doing "Constructor dependency injection". What this means is that I no longer do field injection by annotating the field with #Inject but pass the dependencies in the constructor like so:
public class MyClassDoingSomething implements DoSomethig {
private final Service mainService;
#Inject
public MyClassDoingSomething(Service mainService) {
this.mainService = mainService;
}
}
Notice how the constructor now receives the parameter and sets the field to it and is also annotated with #Inject? I also like to make these classes implement an interface (also for MyService) - Amongst several other benefits I find it makes the dagger module easier to write:
#Module
public class DoSomethingModule {
#Provides #Singleton public RestService provideRestService() {
return new RestService();
}
#Provides #Singleton public MyPrinter provideMyPrinter() {
return new MyPrinter();
}
#Provides #Singleton public Service provideMyPrinter(MyService service) {
return service;
}
#Provides #Singleton public DoSomethig provideMyPrinter(MyClassDoingSomething something) {
return something;
}
}
(This assumes that MyService implements or extends Service)
By now it seems you already know that dagger is able to figure out the dependency graph by itself and build all the objects for you. So what about unit testing the class MyClassDoingSomething? I don't even use dagger here. I simply provide the dependencies manually:
public class MyClassDoingSomethingTest {
#Mock
Service service;
private MyClassDoingSomething something;
#Before
public void setUp() throws Exception {
MockitoAnnotations.init(this);
something = new MyClassDoingSomething(service);
}
// ...
}
As you see, the dependency is passed through the constructor manually.
Obviously this doesn't work if you're coding something that doesn't have a constructor that can be invoked by you. Classical examples are android activities, fragments or views. There are ways to achieve that, but personally I still think you can somehow overcome this without dagger. If you are unit testing a view that has a field #Inject MyPresenter myPresenter, usually this field will have package access that works fine in the tests:
public class MyViewTest {
#Mock MyPresenter presenter;
private MyView view;
#Before
public void setUp() throws Exception {
MockitoAnnotations.init(this);
view.myPresenter = presenter;
}
}
Note that this only works if both MyViewTest and MyView are in the same package (which often is the case in android projects).
At the end of the day if you still want to use dagger for the tests, you can always create "test" modules and components that can inject by declaring methods in the component like:
#Inject
public interface MyTestComponent {
void inject(MyClassDoingSomething something);
}
I find this approach ok-ish, but throughout my development years I prefer the first approach. This also has reported issues with Robolectric that some setup in the build.gradle file is required to actually make the dagger-compiler run for the tests so the classes are actually generated.

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.

How to make autowire object initialized in ApplicationListener?

I want to read data in ApplicationListener, but my object is not initialized. Below is my code:
AppContextListener.java
#Component
public class AppContextListener implements ApplicationListener<ContextRefreshedEvent> {
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
AppContext.getInstance();
}
}
AppContext.java
public class AppContext {
private static AppContext instance;
#Autowired
MyElasticsearchRepository repository;
public AppContext(){
InitData();
}
public static synchronized AppContext getInstance() {
if (instance == null) {
instance = new AppContext();
}
return instance;
}
private void InitData(){
List<MyEntity> dataList = repository.findAllEntities();//repository is null here
//.......
}
}
MyElasticsearchRepository.java
public interface MyElasticsearchRepository extends ElasticsearchRepository<MyEntity,String>
{ }
problem
As you can see in my code, at InitData(), repository is null. I don't
know why #Autowired MyElasticsearchRepository repository; does not
worked here.
Please show me how to fix this. Thank you very much.
There are a couple of things wrong with your code.
First you are using the singleton pattern which I would say is an anti-pattern especially when combined with auto wiring.
Second in your getInstance() method you are creating a new instance of AppContext yourself. This instance isn't managed by Spring so #Autowired is pretty much useless here, Spring is only able to inject dependencies into beans it knows about.
Instead make your AppContext a component (or service what ever you like). Remove the getInstance method and use constructor injection instead.
#Component
public class AppContext {
private final MyElasticsearchRepository repository;
#Autowired
public AppContext(MyElasticsearchRepository repository){
this.repository=repository;
}
...
}
Thirdly you are trying to use the #Autowired instance from the constructor (you are doing method call which expects it to be there), however auto wiring can only be done on an instance of a bean. So at that moment the auto wiring hasn't taken place and your variable will always be null. Instead of calling the method from the constructor either, use constructor inject or annotate the InitData method with #PostConstruct.
#PostConstruct
private void InitData(){
List<MyEntity> dataList = repository.findAllEntities();
...
}
Now that your AppContext is a component it will be detect by spring and you can simply inject it into your ApplicationListener.
#Component
public class AppContextListener implements ApplicationListener<ContextRefreshedEvent> {
private final AppContext appContext;
#Autowired
public AppContextListener(AppContext appContext) {
this.appContext=appContext;
}
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
// Do your thing with appContext
}
}
Note: I prefer constructor injection for required fields and setter injection for optional fields. You should avoid field injection (i.e. #Autowired on instance fields) as that is considered a bad practice. See here why field injection is evil and should be avoided.
#Autowired will only work if bean is marked with Stereotype annotation (What's the difference between #Component, #Repository & #Service annotations in Spring?) or you explicitly define it in spring configuration.
AppContextListener.java
#Component // AFAIR not needed. Spring will create this bean when it will see that class implements `ApplicationListener` interface.
public class AppContextListener implements ApplicationListener<ContextRefreshedEvent> {
#Autowired
private AppContext appContext;
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
appContext.initData();
}
}
AppContext.java
#Component
public class AppContext {
#Autowired
MyElasticsearchRepository repository;
public void initData(){
List<MyEntity> dataList = repository.findAllEntities();//repository is null here
//.......
}
}
#Autowired will work only after AppContext object constructed. Since you try to access #Autowired element inside constructor, it doesn't exist.
Can't you just do this?
#Component
public class AppContextListener implements ApplicationListener<ContextRefreshedEvent> {
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ApplicantContext context = event.getApplicationContext();
MyElasticsearchRepository repository = context.getBean(MyElasticSearchRepository.class);
//do stuff
}
}
http://docs.spring.io/autorepo/docs/spring/4.1.4.RELEASE/javadoc-api/org/springframework/context/event/ContextRefreshedEvent.html

Wicket Dependency Injection

I've got a page with a form in Wicket where the form requires a collaborator to get its job done. The collaborator is injected (for which I'm using Guice) and looks something like:
public class RegistrationPage extends WebPage {
#Inject
public RegistrationPage(RegistrationService service) {
this.service = service;
add(new RegistrationForm());
}
private class RegistrationForm extends Form {
public RegistrationForm() {
// setup
}
protected void onSubmit() {
service.doSomething();
}
}
}
I don't like the idea that the RegistrationService is injected into the RegistrationPage when it's just the RegistrationForm that needs it. I could change the RegistrationForm to receive the RegistrationService:
public RegistrationForm(RegistrationService service) {
this.service = service;
}
and remove the field from the RegistrationPage, but the RegistrationPage is still being used to do the pass-through.
I guess what I'm asking is what the best-practise is for doing this? Is this ok to do, or would it perhaps be better to inject the RegistrationForm itself into the Page:
public class RegistrationPage extends WebPage {
#Inject
public RegistrationPage(RegistrationForm form) {
add(form);
}
}
---
private class RegistrationForm extends Form {
private RegistrationService service;
#Inject
public RegistrationForm(RegistrationService service) {
this.service = service;
}
protected void onSubmit() {
service.doSomething();
}
}
I'd prefer this as I'd like to have the RegistrationForm in a separate class/file. I'm quite new to Wicket so unsure of what the norm is - can someone show me the guiding light? :)
the basic paradigm with wicket+ioc is: most dependencies should be injected via setter injection. constructor injection is impossible for WebPages.
components/panels/forms/pages should only be on the recieving end.
so, inject the dependency to RegistrationService happily into the RegistrationForm , then create it in the RegistrationPage with add(new RegistrationForm());
wicket has IComponentInstantiationListener - one of them is guice. they get notified during the constructor of each component/webpage. so your RegistrationForm will have its dependencies injected before any part of your code can execute.
the way i would do it:
(of course RegistrationForm can be in another file)
public class RegistrationPage extends WebPage {
#Inject
public RegistrationPage() {
add(new RegistrationForm());
}
---
private static class RegistrationForm extends Form {
RegistrationService service;
#Inject
public void setRegistrationService (RegistrationService service){
this.service = service;
}
public RegistrationForm() {
// setup
}
protected void onSubmit() {
service.doSomething();
}
}
}
if you decide to put the RegistrationForm inside the Page as inner class, remember to declare it static! you will most likely not need any references to the enclosing class.
In my opinion DI on Wicket has one confusing element, is done automagically on Components or Pages but not in Models. In my opinion exactly Model (but not page) should have dependency to JPA etc.
Official doc say to use InjectorHolder.getInjector().inject(this)

Categories

Resources