43

I want to create a Spring bean in Spring Java configuration with some constructor arguments passed at runtime. I have created the following Java config, in which there is a bean fixedLengthReport that expects some arguments in constructor.

@Configuration
public class AppConfig {

    @Autowrire
    Dao dao;

    @Bean
    @Scope(value = "prototype")
    **//SourceSystem can change at runtime**
    public FixedLengthReport fixedLengthReport(String sourceSystem) {
         return new TdctFixedLengthReport(sourceSystem, dao);
    }
}

But i am getting error that sourceSystem couldn't wire because no bean found. How can I create bean with runtime constructor arguments?

I am using Spring 4.2

M. Prokhorov
  • 3,894
  • 25
  • 39
suraj bahl
  • 2,864
  • 6
  • 31
  • 42
  • Where have you defined the bean of `SourceSystem`? – user2004685 Jan 31 '16 at 00:30
  • SourceSystem is not a spring bean. Let's say it is just a string and its value is determined at runtime. i have updated my question – suraj bahl Jan 31 '16 at 04:22
  • Can you provide the implementation of `TdctFixedLengthReport`? – user2004685 Jan 31 '16 at 06:02
  • what IDEA you are using – Haim Raman Jan 31 '16 at 07:02
  • @surajbahl : this error maybe because spring doesnot know about the instance of TdctFixedLengthReport. here the instance of TdctFixedLengthReport is created by the program not by spring. autowired it so the instance of TdctFixedLengthReport is created by spring itself. – mehere Jan 31 '16 at 09:32

3 Answers3

75

You can use a prototype bean along with a BeanFactory.

@Configuration
public class AppConfig {

   @Autowired
   Dao dao;

   @Bean
   @Scope(value = "prototype")
   public FixedLengthReport fixedLengthReport(String sourceSystem) {
       return new TdctFixedLengthReport(sourceSystem, dao);
   }
}

@Scope(value = "prototype") means that Spring will not instantiate the bean right on start, but will do it later on demand. Now, to customize an instance of the prototype bean, you have to do the following.

@Controller
public class ExampleController{

   @Autowired
   private BeanFactory beanFactory;

   @RequestMapping("/")
   public String exampleMethod(){
      TdctFixedLengthReport report = 
         beanFactory.getBean(TdctFixedLengthReport.class, "sourceSystem");
   }
}

Note, because your bean cannot be instantiated on start, you must not Autowire your bean directly; otherwise Spring will try to instantiate the bean itself. This usage will cause an error.

@Controller
public class ExampleController{

   //next declaration will cause ERROR
   @Autowired
   private TdctFixedLengthReport report;

}
jaco0646
  • 15,303
  • 7
  • 59
  • 83
Ken Bekov
  • 13,696
  • 3
  • 36
  • 44
  • 3
    please change the typo in `@Autowire` Annotation. (and yes, this bugged me a LOT! ;) ) – Dominik Jan 31 '16 at 14:03
  • 2
    @Dominik thanx a lot. Next time feel free to edit my post, if you'll find same typos. ) – Ken Bekov Jan 31 '16 at 14:05
  • Seems an edit must edit at least 6 characters, at least that's what happened to me when I tried to edit the question (1 char) and your post (3 chars edited) :-) – Dominik Jan 31 '16 at 14:08
  • @KenBekov it’s alreday prototype scope, will be initiated when called, why we do need for lazy? Thx – Spring Feb 09 '18 at 14:48
  • 2
    @Spring you right. `Prototype` will be initiated when call. Therefore `Lasy` is not needed for `Prototype` bean. It's just to demonstrate concept, but actually `Lazy` here is a kind of redundancy. – Ken Bekov Feb 10 '18 at 02:22
  • Another [answer agrees](https://stackoverflow.com/a/2365720/1371329) that `@Lazy` is redundant on prototype beans. I suggest removing it to avoid confusion. – jaco0646 Jan 26 '19 at 00:29
  • I don't know why but the bean factory is not autowired in my case ( NullPointerException ) – Mehdi Jul 10 '20 at 05:32
  • Is there any way to provide some arguments by other Beans and others dynamically? Example: `@Bean fun createContact(number: Int, name: String): Contact;`. Let's say `number` is provided by another `@Bean` and I want to only provide the `name`, is this possible in any way? – Farid Sep 22 '22 at 08:49
12

This can be achieved with Spring's ObjectProvider<> class which was introduced in Spring 4.3. See Spring's documentation for additional details.

The gist is to define the bean factory method for the object to be provided, inject the ObjectProvider<> in your consumer and create new instances of the object to be provided.

public class Pair
{
    private String left;
    private String right;

    public Pair(String left, String right)
    {
        this.left = left;
        this.right = right;
    }

    public String getLeft()
    {
        return left;
    }

    public String getRight()
    {
        return right;
    }
}

@Configuration
public class MyConfig
{
    @Bean
    @Scope(BeanDefinition.SCOPE_PROTOTYPE)
    public Pair pair(String left, String right)
    {
        return new Pair(left, right);
    }
}

@Component
public class MyConsumer
{
    private ObjectProvider<Pair> pairProvider;

    @Autowired
    public MyConsumer(ObjectProvider<Pair> pairProvider)
    {
        this.pairProvider = pairProvider;
    }

    public void doSomethingWithPairs()
    {
        Pair pairOne = pairProvider.getObject("a", "b");
        Pair pairTwo = pairProvider.getObject("z", "x");
    }
}

NOTE: you don't actually implement the ObjectProvider<> interface; Spring does that for you automagically. You just need to define the bean factory method.

HairOfTheDog
  • 2,489
  • 2
  • 29
  • 35
5

You code looks fine, to get the prototype with parameters use the BeanFactory#getBean(String name, Object... args) method.

Look at Spring Java Config: how do you create a prototype-scoped @Bean with runtime arguments? BeanFactory#getBean(String name, Object... args) would be what you are looking for.

I guess that your IDEA (in my case IntelliJ IDEA version 15.) give you the error and it’s not a runtime/compile time error.

In IntelliJ you can change the setting of Spring inspections.

  • Go to file -> settings.
  • Type inspections in the search box.
  • Go to Spring Core->Code->Autowire for Bean Classes.
  • Change from "Error" to “weak warning”
Community
  • 1
  • 1
Haim Raman
  • 11,508
  • 6
  • 44
  • 70
  • Correct, I am using idea version 15 so it could be because of that. I would change the settings to remove it. Thanks for your help Haim. – suraj bahl Jan 31 '16 at 17:27
  • The error has bothered me for some time. Thanks for showing the way to turn it off! – B.Z. Sep 17 '21 at 15:27