Home > database >  Create and Autowire the component with constructor
Create and Autowire the component with constructor

Time:11-29

I have the below class

@Component
public class MyBean {
    
     private int val1;
    
    

    public MyBean(int val1) {
        this.val1 = val1;
    }

    public int getVal1() {
        return val1;
    }

    public void setVal1(int val1) {
        this.val1 = val1;
    }
    
    
    
    
    
    

}

I want to Autowire Mybean like below

@Service
public class MyService
{
    @Autowire
    private MyBean myBean;
}

When i run i get the below error

Parameter 0 of constructor MyBean required a bean of type 'int' that could not be found.

CodePudding user response:

There are many ways to achieve it, all of this will lead to inject proper instance of MyBean to the IOC container.

  • Bean definition with annotation processor with @Configuration

Indicates that a class declares one or more @Bean methods and may be processed by the Spring container to generate bean definitions.

@Configuration
public class Configuration {
    
    @Bean
    MyBean beanName() {
        return new MyBean('some_value');
    }
    
}

They are many ways to access the IOC container itself, directly via fluent api's or indirectly via implementing bean aware interfaces like InitializingBean, specifically use this if you have some operations just after creating bean instance.

@Configuration
public class Configuration implements InitializingBean
{


    @Bean
    public MyBean beanName() {
        return new MyBean('some_value');
    }

    @Override public void afterPropertiesSet() throws Exception
    {
    //some operations
    ...
    }
}

As mentioned in previous approach this workaround is direct accessing to the IOC container, let imagine you want singelton scope and the canonical name of your bean is MyBean

ConfigurableListableBeanFactory beanFactory = ((ConfigurableApplicationContext) applicationContext).getBeanFactory();
beanFactory.registerSingleton(MyBean, new MyBean('some_value'));

Before Spring 3.0, XML was the only way to define and configure beans, now days it not used anymore.

<bean >
    <property name="val1" value="some_value"/>
</bean>

CodePudding user response:

When autowiring by constructor, Spring considers every parameter as a bean. In other words, and as the trace reads: There is not a @Bean of type int available in the project/context, so it can't build MyBean.

If you need MyBean initialized with a primitive int, then you should use @Bean annotation within a @Configuration class instead, so you can pass the value.

@Configuration
public class MyConfiguration {

    @Bean
    public MyBean myBean() { // MyBean must not have @Component annotation
        return new MyBean(1);
    }
 
}

Additionally, and given that the property already has getters and setter, you can change the type of val1 to Integer, remove the constructor or declare it without parameters, and set it's value in MyService.

@Component
public class MyBean {
    
    private Integer val1;

    public MyBean() {
        // Empty constructor, might be removed
    }

    public int getVal1() {
        return val1;
    }

    public void setVal1(int val1) {
        this.val1 = val1;
    }

}

@Service 
public class MyService { 

    @Autowired private MyBean myBean;

    /* This method is called AFTER the constructor and dependency injections */
    @PostConstruct
    public void initialize() {
        myBean.setVal1(1);
    }

}

Depending on the use-case, however, MyBean could be a POJO, if you need to create it with a primitive value. Not every class in a Spring project needs to be a Bean, after all.

CodePudding user response:

I think your error occurs because spring tells you it doesn't know which integer-value to use in order to instantiate the Bean MyBean. You could do the following:

a) Set a default value directly in the bean and use a no-args-constructor:

@Component
public class MyBean {
    
    private int val1 = 1; // or...
    private int val1; // default initialisation to 0

    public MyBean() {}
    ...
}

b) Provide a default constructor without parameters for MyBean and inject the value as property from a properties file:

@Component
public class MyBean {
    
    @Value("${my.property.name}")
    private int val1;

    public MyBean() {}
    
    public MyBean(int val1) {
        this.val1 = val1;
    }
    ...
}

Then in your root-resource directory you need to have a properties file:

src/main/resource/application.properties

with the content:

my.property.name=1

Read more about properties in this article: https://www.baeldung.com/properties-with-spring

c) possibly many other options...those will depend on your specific use case. We don't know enough about your class and which int-values are suitable.

CodePudding user response:

No-args Constructor

You can introduce a no-args default constructor in the MyBean class, Spring would make use of it and val1 would have a value of 0 (since it's a default for int type).

@Bean

Or you can create a method annotated with @Bean inside a configuration class and provide through it an instance of MyBean with whatever value of val1 you need.

Note that if you would choose this option, you would need to remove @Component annotation from the MyBean class.

Example:

@Configuration
public class MyConfig {
    
    @Bean
    MyBean myBean() {
        return new MyBean(1_000);
    }
}

Defining multiple Bean of the same type

Also, note that using @Bean you can define as many flavors of MyBean as you need. And another advantage is that MyBean not necessarily should be your domain class (it can come from a third party library or JDK), whilst stereotype annotations can be placed only on top of your custom classes.

Here's an example:

@Configuration
public class BeanConfig {
    
    @Bean
    @Qualifier("1000")
    MyBean myBean1() {
        return new MyBean(1_000);
    }
    
    @Bean
    @Qualifier("8000")
    MyBean myBean2() {
        return new MyBean(8_000);
    }
}

@Qualifier annotation is needed to distinguish these beans. It allows using various flavors of your bean in different parts of the application.

Let's make use of the bean with a value of 8000 in the service:

@Service
public class MyService {
    private MyBean myBean; // field injection is a not a good practice
    
    public MyService(@Qualifier("8000") MyBean myBean) {
        System.out.println(myBean); // would allow to ditermin which bean is used here (don't forget to override toString)
        this.myBean = myBean;
    }
}

Note using @Autowired on field is not a recommended practice. The common approach is to use constructor injection, and in case if there's only one constructor in the class using @Autowired is not required.

  • Related