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

I'm using Spring 3.0.5 and am using @Autowire annotation for my class members as much as possible. One of the beans that I need to autowire requires arguments to its constructor. I've looked through the Spring docs, but cannot seem to find any reference to how to annotate constructor arguments.

In XML, I can use as part of the bean definition. Is there a similar mechanism for @Autowire annotation?

Ex:

@Component
public class MyConstructorClass{

  String var;
  public MyConstructorClass( String constrArg ){
    this.var = var;
  }
...
}


@Service
public class MyBeanService{
  @Autowired
  MyConstructorClass myConstructorClass;

  ....
}

In this example, how do I specify the value of "constrArg" in MyBeanService with the @Autowire annotation? Is there any way to do this?

Thanks,

Eric

The answer is


I like Zakaria's answer, but if you're in a project where your team doesn't want to use that approach, and you're stuck trying to construct something with a String, integer, float, or primative type from a property file into the constructor, then you can use Spring's @Value annotation on the parameter in the constructor.

For example, I had an issue where I was trying to pull a string property into my constructor for a class annotated with @Service. My approach works for @Service, but I think this approach should work with any spring java class, if it has an annotation (such as @Service, @Component, etc.) which indicate that Spring will be the one constructing instances of the class.

Let's say in some yaml file (or whatever configuration you're using), you have something like this:

some:
    custom:
        envProperty: "property-for-dev-environment"

and you've got a constructor:

@Service // I think this should work for @Component, or any annotation saying Spring is the one calling the constructor.
class MyClass {
...
    MyClass(String property){
    ...
    }
...
}

This won't run as Spring won't be able to find the string envProperty. So, this is one way you can get that value:

class MyDynamoTable
import org.springframework.beans.factory.annotation.Value;
...
    MyDynamoTable(@Value("${some.custom.envProperty}) String property){
    ...
    }
...

In the above constructor, Spring will call the class and know to use the String "property-for-dev-environment" pulled from my yaml configuration when calling it.

NOTE: this I believe @Value annotation is for strings, intergers, and I believe primative types. If you're trying to pass custom classes (beans), then approaches in answers defined above work.


You can also configure your component like this :

package mypackage;
import org.springframework.context.annotation.Configuration;
   @Configuration
   public class MyConstructorClassConfig {


   @Bean
   public MyConstructorClass myConstructorClass(){
      return new myConstructorClass("foobar");
   }
  }
}

With the Bean annotation, you are telling Spring to register the returned bean in the BeanFactory.

So you can autowire it as you wish.


In this example, how do I specify the value of "constrArg" in MyBeanService with the @Autowire annotation? Is there any way to do this?

No, not in the way that you mean. The bean representing MyConstructorClass must be configurable without requiring any of its client beans, so MyBeanService doesn't get a say in how MyConstructorClass is configured.

This isn't an autowiring problem, the problem here is how does Spring instantiate MyConstructorClass, given that MyConstructorClass is a @Component (and you're using component-scanning, and therefore not specifying a MyConstructorClass explicitly in your config).

As @Sean said, one answer here is to use @Value on the constructor parameter, so that Spring will fetch the constructor value from a system property or properties file. The alternative is for MyBeanService to directly instantiate MyConstructorClass, but if you do that, then MyConstructorClass is no longer a Spring bean.


You need to use @Autowired and @Value. Refer this post for more information on this topic.


Another alternative, if you already have an instance of the object created and you want to add it as an @autowired dependency to initialize all the internal @autowired variables, could be the following:

@Autowired 
private AutowireCapableBeanFactory autowireCapableBeanFactory;

public void doStuff() {
   YourObject obj = new YourObject("Value X", "etc");
   autowireCapableBeanFactory.autowireBean(obj);
}

Most answers are fairly old, so it might have not been possible back then, but there actually is a solution that satisfies all the possible use-cases.

So right know the answers are:

  • Not providing a real Spring component (the factory design)
  • or does not fit every situation (using @Value you have to have the value in a configuration file somewhere)

The solution to solve those issues is to create the object manually using the ApplicationContext:

@Component
public class MyConstructorClass
{
    String var;

    public MyConstructorClass() {}
    public MyConstructorClass(String constrArg) {
        this.var = var;
    }
}

@Service
public class MyBeanService implements ApplicationContextAware
{
    private static ApplicationContext applicationContext;

    MyConstructorClass myConstructorClass;

    public MyBeanService()
    {
        // Creating the object manually
        MyConstructorClass myObject = new MyConstructorClass("hello world");
        // Initializing the object as a Spring component
        AutowireCapableBeanFactory factory = applicationContext.getAutowireCapableBeanFactory();
        factory.autowireBean(myObject);
        factory.initializeBean(myObject, myObject.getClass().getSimpleName());
    }

    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        applicationContext = context;
    }
}

This is a cool solution because:

  • It gives you access to all the Spring functionalities on your object (@Autowired obviously, but also @Async for example),
  • You can use any source for your constructor arguments (configuration file, computed value, hard-coded value, ...),
  • It only requires you to add a few lines of code without having to change anything.
  • It can also be used to dynamically create a unknown number of instances of a Spring-managed class (I'm using it to create multiple asynchronous executors on the fly for example)

The only thing to keep in mind is that you have to have a constructor that takes no arguments (and that can be empty) in the class you want to instantiate (or an @Autowired constructor if you need it).


An alternative would be instead of passing the parameters to the constructor you might have them as getter and setters and then in a @PostConstruct initialize the values as you want. In this case Spring will create the bean using the default constructor. An example is below

@Component
public class MyConstructorClass{

  String var;

  public void setVar(String var){
     this.var = var;
  }

  public void getVar(){
    return var;
  }

  @PostConstruct
  public void init(){
     setVar("var");
  }
...
}


@Service
public class MyBeanService{
  //field autowiring
  @Autowired
  MyConstructorClass myConstructorClass;

  ....
}

Well, from time to time I run into the same question. As far as I know, one cannot do that when one wants to add dynamic parameters to the constructor. However, the factory pattern may help.

public interface MyBean {
    // here be my fancy stuff
}

public interface MyBeanFactory {
    public MyBean getMyBean(/* bean parameters */);
}

@Component
public class MyBeanFactoryImpl implements MyBeanFactory {
    @Autowired
    WhateverIWantToInject somethingInjected;

    public MyBean getMyBean(/* params */) {
        return new MyBeanImpl(/* params */);
    }

    private class MyBeanImpl implements MyBean {
        public MyBeanImpl(/* params */) {
            // let's do whatever one has to
        }
    }
}

@Component
public class MyConsumerClass {
    @Autowired
    private MyBeanFactory beanFactory;

    public void myMethod() {
        // here one has to prepare the parameters
        MyBean bean = beanFactory.getMyBean(/* params */);
    }
}

Now, MyBean is not a spring bean per se, but it is close enough. Dependency Injection works, although I inject the factory and not the bean itself - one has to inject a new factory on top of his own new MyBean implementation if one wants to replace it.

Further, MyBean has access to other beans - because it may have access to the factory's autowired stuff.

And one might apparently want to add some logic to the getMyBean function, which is extra effort I allow, but unfortunately I have no better solution. Since the problem usually is that the dynamic parameters come from an external source, like a database, or user interaction, therefore I must instantiate that bean only in mid-run, only when that info is readily available, so the Factory should be quite adequate.


Examples related to spring

Are all Spring Framework Java Configuration injection examples buggy? Two Page Login with Spring Security 3.2.x Access blocked by CORS policy: Response to preflight request doesn't pass access control check Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean Failed to auto-configure a DataSource: 'spring.datasource.url' is not specified Spring Data JPA findOne() change to Optional how to use this? After Spring Boot 2.0 migration: jdbcUrl is required with driverClassName The type WebMvcConfigurerAdapter is deprecated No converter found capable of converting from type to type

Examples related to constructor

Two constructors Class constructor type in typescript? ReactJS: Warning: setState(...): Cannot update during an existing state transition Inheritance with base class constructor with parameters What is the difference between using constructor vs getInitialState in React / React Native? Getting error: ISO C++ forbids declaration of with no type undefined reference to 'vtable for class' constructor Call asynchronous method in constructor? Purpose of a constructor in Java? __init__() missing 1 required positional argument

Examples related to autowired

intellij incorrectly saying no beans of type found for autowired repository How do I mock an autowired @Value field in Spring with Mockito? @Autowired - No qualifying bean of type found for dependency Spring can you autowire inside an abstract class? Why is my Spring @Autowired field null? Understanding Spring @Autowired usage @Autowired and static method Injecting @Autowired private field during testing Spring expected at least 1 bean which qualifies as autowire candidate for this dependency Exclude subpackages from Spring autowiring?

Examples related to spring-annotations

Spring Boot @Value Properties How to inject a Map using the @Value Spring Annotation? @Autowired - No qualifying bean of type found for dependency at least 1 bean Populating Spring @Value during Unit Test Spring MVC @PathVariable with dot (.) is getting truncated Is there a way to @Autowire a bean that requires constructor arguments? What does the @Valid annotation indicate in Spring?

Examples related to spring-bean

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