Registering an instance as 'singleton' bean at application startup

僤鯓⒐⒋嵵緔 提交于 2021-02-07 17:13:28

问题


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.


回答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; }
    }

}



回答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.



来源:https://stackoverflow.com/questions/40869870/registering-an-instance-as-singleton-bean-at-application-startup

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!