How to Autowire a Component Which Is Having Constructor With Arguments in Springboot Application

Is there a way to @Autowire a bean that requires constructor arguments?

You need the @Value annotation.

A common use case is to assign default field values using
"#{systemProperties.myProp}" style expressions.

public class SimpleMovieLister {

private MovieFinder movieFinder;
private String defaultLocale;

@Autowired
public void configure(MovieFinder movieFinder,
@Value("#{ systemProperties['user.region'] }") String defaultLocale) {
this.movieFinder = movieFinder;
this.defaultLocale = defaultLocale;
}

// ...
}

See: Expression Language > Annotation Configuration


To be more clear: in your scenario, you'd wire two classes, MybeanService and MyConstructorClass, something like this:

@Component
public class MyBeanService implements BeanService{
@Autowired
public MybeanService(MyConstructorClass foo){
// do something with foo
}
}

@Component
public class MyConstructorClass{
public MyConstructorClass(@Value("#{some expression here}") String value){
// do something with value
}
}

Update: if you need several different instances of MyConstructorClass with different values, you should use Qualifier annotations

How to Autowire a Component which is having constructor with arguments in SpringBoot Application

package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class Transformer {
private String datasource;

@Autowired
public Transformer(String datasource) {
this.datasource=datasource;
log.info(datasource);
}
}

Then create a config file

package com.example.demo;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class BeanConfig {
@Bean
public Transformer getTransformerBean() {
return new Transformer("hello spring");
}

@Bean
public String getStringBean() {
return new String();
}
}

Autowired on Constructor with parameters fails?

@Autowired
public Bean1(String name, int id) {
System.out.println("Inside Bean1(String name, int id) constructor");
this.name = name;
this.id = id;
}

Tells to the container to inject 2 beans (one bean of type String called name and the other one called id of type int) when the constructor is used to instantiate bean1.
Where did you defined those beans? Also note that creating beans from primitive types is not common, that does not mean you can't.

For primitive type we generally declared them in application.properties and then inject them using @Value.

How do constructor calls with @Autowired work?

TL;DR

When creating beans Spring will have to invoke constructors that the target bean class contains :

  1. If there is no constructor defined - then Spring will invoke the implicit default constructor generated by compiler.
  2. If there is a no-args constructor defined explicitly then Spring invoke this one since there is no other constructor defined.
  3. If there is a constructor defined, which requires some dependencies then Spring will have to invoke this one and provide dependencies for it. (Since Spring 4.3 you do not even have to mark this constructor with @Autowired).
  4. If there are multiple args constructor defined then you will have to resolve ambiguity, since Spring will not know which one to choose. (Then you can mark one of them with @Autowired or use configuration class to define your beans).

Side notes

Spring IOC container (application context) is responsible for holding beans and return them whenever it is asked to do so. To create a context you have to tell Spring where to look for bean definitions : you can provide xml file, java configuration or enable auto-scanning of components in given packages. When Spring context is being created it has to create beans. It will try to invoke constructors and provide any dependencies for beans that require them.

In your example when instance of MyClass will be created for the context, it will invoke default constructor of MyClass class and then set it's dependency via reflection.

However field injection is typically a bad idea as you might have problems with testing such components. Constructor or setter injection is a better choice.

If you changed your MyClass to :

public class MyClass {

private MyService service;

@Autowired
public MyClass(MyService service) {
this.service = service;
}

}

here you provide your own constructor - note that there will be no default constructor generated in this case. So Spring will have to invoke constructor you provided and satisfy dependency for it. If there is no dependency that can be injected - an exception will be thrown.

Notice that you can use your classes even without Spring :

MyService myService = new MyService();
MyClass myclass = new MyClass(myService);

By marking your classes with Spring stereotypes and by using @Autowired you just enable spring support for context creation and dependency injection (in case of automated package scanning)

How to autowire a service in Spring Boot and pass dynamic parameters to the constructor

I fixed this by changing the implementation of the class so that the execute method receives the corresponding parameters, and the autowired fields are the only attributes of the StealFromPocket class. Now it looks like this:

@Component
public class StealFromPocket {

@Autowired
private GameService gameService;

public void execute(Player playerFrom) {
Integer gameId = playerFrom.getGame().getId();
gameService.findPlayersByGameId(gameId).forEach(player -> {
new StealCoinCommand(playerFrom, player).execute();
});
}
}

And in the GameService.java I do the following:

// Get the class from its name
Class<?> clazz = Class.forName(completeClassName);
// Instantiate an object of the class
Object cardCommand = clazz.getDeclaredConstructor().newInstance();
// Autowire the new object's dependencies (services used inside)
AutowireCapableBeanFactory factory = applicationContext.getAutowireCapableBeanFactory();
factory.autowireBean(cardCommand);
factory.initializeBean(cardCommand, className);
// Get a reference to the method "execute", that receives 2 parameters
Method method = clazz.getDeclaredMethod("execute", Player.class);
// Execute the method with the parameters
method.invoke(cardCommand, playerFrom);

Springboot using @Autowired on constructor with parameter

Your class is annotated with @SpringBootApplication which is also @Configuration. And @Configuration should have a default no-args constructor.
From javadoc:

@Configuration classes must have a default/no-arg constructor and may
not use @Autowired constructor parameters.

Since Spring version 4.3 you can have constructor injection for @Configuration class. Tested on Spring Boot version 1.5.3 and it works fine.

Here are the release notes for Spring 4.3. And here is the feature that you need:

@Configuration classes support constructor injection.

Autowire Bean with constructor parameters

The constructor for Bean needs to be annotated with @Autowired or @Inject, otherwise Spring will try to construct it using the default constructor and you don't have one of those.

The documentation for @Autowired says that it is used to mark a constructor, field, setter method or config method as to be autowired by Spring's dependency injection facilities. In this case you need to tell Spring that the appropriate constructor to use for autowiring the dependency is not the default constructor. In this case you're asking Spring to create SecondBean instance, and to do that it needs to create a Bean instance. In the absence of an annotated constructor, Spring will attempt to use a default constructor.

http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html



Related Topics



Leave a reply



Submit