Registering an instance as 'singleton' bean at application startup

11,932

Solution 1

As described in the comment this can be done by storing your location details on a configuration file and then inject them upon Spring Bean initialization.

Assuming your application.properties looks like this:

my.sample.config.A=somelocationA
my.sample.config.B=somelocationB
my.sample.config.C=somelocationC
my.sample.config.D.one=somelocationD1
my.sample.config.D.two=somelocationD2

Below I'm demo-ing 4 ways to do achieve this:

1.By injecting your property directly on the Bean method creation:

@Bean
public A myBeanA(@Value("${my.sample.config.A}") String myprop) {
    System.out.println("from bean A with " + myprop);
    return new A(myprop);
}

2.By injecting the property on a Config-wide variable and use that in your Bean method creation:

@Value("${my.sample.config.B}")
private String mylocationB;
//..
@Bean
public B myBeanB() {
    System.out.println("from bean B with " + mylocationB);
    return new B(mylocationB);
}

3.By injecting the whole environment in the Config and then hand-pick the property needed:

@Autowired
private Environment env;
//..
    @Bean 
    public C myBeanC() {
    String locationC = env.getProperty("my.sample.config.C");
    System.out.println("from bean C with " + locationC);
    return new C(locationC);
}

4.This is a Spring Boot exclusive way. You can use Type-safe Configuration Properties annotating with @ConfigurationProperties directly your bean defining a prefix-namespace and all params from that point onwards will be auto-mapped to the properties defined in that bean!

@ConfigurationProperties(prefix = "my.sample.config.D")
@Component
class D {
    private String one;
    private String two;

    public String getOne() { return one; }

    public void setOne(String one) {
        System.out.println("from bean D with " + one);
        this.one = one;
    }
    public String getTwo() { return two; }

    public void setTwo(String two) {
        System.out.println("from bean D with " + two);
        this.two = two;
    }
}

Below the overall one-file code in one piece:

package com.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

@SpringBootApplication
public class DemoApplication {

    @Autowired
    private Environment env;

    @Value("${my.sample.config.B}")
    private String mylocationB;

    @Bean
    public A myBeanA(@Value("${my.sample.config.A}") String myprop) {
        System.out.println("from bean A with " + myprop);
        return new A(myprop);
    }

    @Bean
    public B myBeanB() {
        System.out.println("from bean B with " + mylocationB);
        return new B(mylocationB);
    }

    @Bean
    public C myBeanC() {
        String locationC = env.getProperty("my.sample.config.C");
        System.out.println("from bean C with " + locationC);
        return new C(locationC);
    }

    @ConfigurationProperties(prefix = "my.sample.config.D")
    @Component
    class D {
        private String one;
        private String two;

        public String getOne() { return one; }

        public void setOne(String one) {
            System.out.println("from bean D with " + one);
            this.one = one;
        }
        public String getTwo() { return two; }

        public void setTwo(String two) {
            System.out.println("from bean D with " + two);
            this.two = two;
        }
    }

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    class A {
        private final String location;
        public A(String location) { this.location = location; }
    }

    class B {
        private final String location;
        public B(String location) { this.location = location; }
    }

    class C {
        private final String location;
        public C(String location) { this.location = location; }
    }

}

Solution 2

In the same file that you have @SpringBootApplication do:

@Bean
public IService service() {
    return new Service("C:\\data.bin");
}

Spring should autowire everything up for you. It should be a singleton by default (http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/html/beans.html#beans-factory-scopes).

Edit 1: You should also annotate your Service implementation with @Service instead of @Component (see: What's the difference between @Component, @Repository & @Service annotations in Spring?).

Edit 2: You also don't necessarily have to put the @Bean method in the class that has @SpringBootApplication. You can put it in any class that has the @Configuration annotation.

Share:
11,932
Andrei Rînea
Author by

Andrei Rînea

Orthodox Christian Passionated about software. Perfectionist, but still pragmatic. Security and Software Engineer. Application Security, .NET, Java and many other technologies

Updated on June 04, 2022

Comments

  • Andrei Rînea
    Andrei Rînea almost 2 years

    I am playing around with Spring Boot and I am trying to construct an instance of ServiceImpl to be resolved when a Service is required. Currently I am annotating the implementation as @Component but this does not give me the chance to construct the instance as I want.

    The ServiceImpl should be constructed with a String containing a path to a file on disk. I would like to do this in the main method of the @SpringBootApplication class of the application.

    Maybe it's just me coming from a long .NET background where we usually setup the IoC container like:

    Service service = new Service("C:\\data.bin");
    container.RegisterSingleton<IService>(service); // now whoever asks for a IService will receive this precise instance
    

    Does this make sense in Spring world?

    LE: I am well aware of the GoF singleton definition (i.e. prevent everyone else from creating instances of the class) - I am not targeting this.

  • Emdadul Sawon
    Emdadul Sawon almost 7 years
    Thanks, nice answer. It will be better, if you explain how to use class D.
  • dimitrisli
    dimitrisli almost 7 years
    @EmdadulSawon Thanks - I've added link to the documentation where you'll find all the details about it.
  • Andrei Rînea
    Andrei Rînea almost 7 years
    ended up using this version :)