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 :
- If there is no constructor defined - then Spring will invoke the implicit default constructor generated by compiler.
- If there is a no-args constructor defined explicitly then Spring invoke this one since there is no other constructor defined.
- 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
). - 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
Using an Empty Keystore Password Used to Be Possible
Java Properties - How to Create Dynamic Properties
Keytool Error Bash: Keytool: Command Not Found
How to Set a Ttl for @Cacheable
Spring Boot Required Request Part 'File' Is Not Present
How to Exclude Classes from Test Coverage Using Java Annotations in Spring Boot
How to Test Class Which Implements Runnable With Junit
Server_Error: [Code] 1675030 [Message]: Error Performing Query
Spring Security 401 Unauthorized Even With Permitall
Spring Data Query Method With Optional @Param
How to Check If a Java 8 Stream Is Empty
Java Input Validation for Number Range and Numeric Values Only With Counter
Object Cannot Be Converted to Integer Error
How to Retrieve Mapping Table Name for an Entity in JPA At Runtime
How to Get Access to Job Parameters from Itemreader, in Spring Batch
Mock User Console Input in Junit Test
Gradle Java9 Could Not Target Platform: 'Java Se 9' Using Tool Chain: 'Jdk 8 (1.8)'