Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

spring: how to use non-default constructor when auto-wiring a prototype bean using @Autowired and context:component-scan?

Tags:

java

spring

Suppose you have a prototype bean class as below:

@Component
@Scope("prototype")
public class Foobar {
    private String foo;
    public Foobar( String foo ) {
        this.foo = foo;
    }
}

So, is it possible to use @Autowired to wire such a bean in another class, which should use the non-default constructor Foobar(String foo) to instantiate the bean?

update
In the example above, the constructor parameter, String foo, is not available from application context but rather dynamic. So, having the constructor annotated with @Autowired and then specifying foo somewhere in the context doesn't seem an ideal solution here.

like image 377
JBT Avatar asked Aug 10 '14 19:08

JBT


People also ask

Can we do Autowired by constructor?

In Spring, you can use @Autowired annotation to auto-wire bean on the setter method, constructor , or a field . Moreover, it can autowire the property in a particular bean. We must first enable the annotation using below configuration in the configuration file.

How do you use an Autowired constructor?

This mode is very similar to byType, but it applies to constructor arguments. Spring container looks at the beans on which autowire attribute is set constructor in the XML configuration file. It then tries to match and wire its constructor's argument with exactly one of the beans name in the configuration file.

How do you make auto writing as non mandatory for a specific bean property?

If you want to make specific bean autowiring non-mandatory for a specific bean property, use required=”false” attribute in @Autowired annotation.

How do you call an Autowired constructor?

P.S: You can also have a constructor with parameters if you use the @Autowired annotation. On this case, Spring will call this constructor to create the bean and pass the required parameters if there are such beans declared that can be autowired into the constructor.


3 Answers

Here are 3 ways to do it, just peek the best one for your case:

Using @Autowired constructors

Better when: you have all you need to build your prototype bean in the context (even for properties such as @Value("${prop}"))

If you want an automatic way to do it, you will need to have everything that is needed to instantiate the bean in the context too (even for a prototype bean). In case you have everything needed in your context you could simply annotate the constructor as @Autowired and Spring will do the rest for your.

@Component
@Scope("prototype")
public class FooBar {

    private Baz baz;

    @Autowired
    public FooBar(Baz baz) {
        this.baz = baz;
    }

}

Using FactoryBeans

Better when: if you are using an XML based context, you will prefer this way.

Another possibility, if you need a personalized way to do it, would be using FactoryBeans. From documentation:

Interface to be implemented by objects used within a BeanFactory which are themselves factories. If a bean implements this interface, it is used as a factory for an object to expose, not directly as a bean instance that will be exposed itself.

The FactoryBean is used by Spring just to build the object you requested (being it a prototype or singleton).

For your case you could have an implementation like:

@Component
public class FooBarFactory implements FactoryBean<FooBar> {

    @Autowired
    private Baz myContextProvidedObject;

    @Override
    public FooBar getObject() throws Exception {
        return new FooBar(myContextProvidedObject, "my parameter");
    }

    @Override
    public Class<?> getObjectType() {
        return FooBar.class;
    }

    @Override
    public boolean isSingleton() {
        return false;
    }

}

And you could simple @Autowired FooBar on other instances of your context.

Using @Configuration

Better when: if you already have your context configured using annotations you will definitely prefer this way.

A third way to do it, and this is my favorite, would be using your @Configuration class. From documentation:

public @interface Configuration: Indicates that a class declares one or more @Bean methods and may be processed by the Spring container to generate bean definitions and service requests for those beans at runtime, for example:

Within that class your could have a method like:

@Configuration
public class MyConfig {

    @Bean
    @Scope("prototype")
    public FooBar fooBar(Baz myContextProvidedObject) {
        return new FooBar(myContextProvidedObject, "my parameter");
    }

}
like image 113
Francisco Spaeth Avatar answered Oct 24 '22 16:10

Francisco Spaeth


You need to annotate your constructor with @Autowired and for the parameter that has to be passed to the constructor when your bean get instantiated, you have to use a non-primitive type parameter:

For example:

@Component
@Scope("prototype")
public class FooBar {

    private DataSource data;

    @Autowired
    public FooBar(DataSource data) {
        this.bdata = data;
    }
}

and then annotate your DataSource with:

@Component("dataSupplier")
public class DataSource {

    public int getData() {}
}
like image 29
LeTex Avatar answered Oct 24 '22 14:10

LeTex


Since component scanning has been enabled, spring will try to automatically discover and register another Foobar bean. However this will fail because spring can't create an object using the defined constructor as you have not auto-wired the constructor / parameters in the constructor. As the second step spring will try to create a Foobar with no-args constructor. Therefore you need to have auto-wired the constructor which you defined in the spring-config file in order to get rid of this error. Please refer to this article for more information on this topic.

like image 41
Fahim Farook Avatar answered Oct 24 '22 14:10

Fahim Farook