1

Introduction

I have some business logic properties in the application.yml file.

They are loaded into the application via a @ConfigurationProperties annotated class.

How could I use these properties in a class which is not a Spring Bean? It cannot be a singleton, because many objects of it must be created during run-time.

Example

application.yml

business.foo: 2

BusinessProperties.java

@ConfigurationProperties("business")
@Getter // lombok
@Setter // lombok
public class BusinessProperties {
    private int foo;
}

TypicalBean.java

@Component
public class TypicalBean {
    private final BusinessProperties properties;

    @Autowired
    public TypicalBean(BusinessProperties properties) {
        this.properties = properties;
    }

    @PostConstruct
    public void printFoo() {
        System.out.println("Foo: " + properties.getFoo()); // "Foo: 2"
    }
}

NonBean.java

public class NonBean {
    public void printFoo() {
        System.out.println("Foo: ???"); // How to access the property?
    }
}

Is there some way to create a non-singleton class, which can have access to configuration (or even other Spring beans) but otherwise works the same as a regular java class? Meaning that I can control its creation, it is collected by the garbage collector if not used anymore, etc.

Snackoverflow
  • 5,332
  • 7
  • 39
  • 69

4 Answers4

1

You can still define the NonBean.class as a Component with Scope.Prototype

@Component
@Scope(value = BeanDefinition.SCOPE_PROTOTYPE)
public class NonBean {
    @Autowired
    public TypicalBean(BusinessProperties properties) {
        this.properties = properties;
    }

    public void printFoo() {
         System.out.println("Foo: " + properties.getFoo());
    }
}

The trick is how you create an instance of NonBean.class. In the code where you'll be creating an instance of NonBean.class, use Spring's ObjectFactory<T>

private final ObjectFactory<NonBean> nonBeanFactory;
...
NonBean nonBean = nonBeanFactory.getObject();

The instantiated nonBean object will have been autowired.

Jean Marois
  • 1,510
  • 11
  • 19
  • Is this a good practice though? Seems like a hack to me... – JohnEye Apr 16 '20 at 18:53
  • @JohnEye Are you asking whether using SCOPE_PROTOTYPE beans is a good practice? Not sure, probably depends on the problem or context, but if you need them the ObjectFactory approach works. – Jean Marois Apr 18 '20 at 01:16
0

All spring-beans creates by SpringApplicationContext. Bean - it's simple POJO-object, but created by Spring and saved in his container. If you want to get access to bean from outside of container - see this:

Getting Spring Application Context

0

Spring beans are really meant to be used within the application context but you might be able to achieve what you want by autowiring the properties to a static field in a Spring bean.

@Component
public class BusinessPropertiesUtils {
    public static BusinessProperties INSTANCE;

    @Autowired
    public setBusinessProperties(BusinessProperties properties) {
        this.INSTANCE = properties;
    }
}

And then:

public class NonBean {
    public void printFoo() {
        System.out.println("Foo: " + BusinessPropertiesUtils.INSTANCE.getFoo());
    }
}

PS: this is very hacky and definitely not the "Spring way".

Christophe L
  • 13,725
  • 6
  • 33
  • 33
0

You can configure beans with the prototype scope, which will give you a new instance of the bean every time it's requested. From the Spring documentation:

In contrast to the other scopes, Spring does not manage the complete lifecycle of a prototype bean. The container instantiates, configures, and otherwise assembles a prototype object and hands it to the client, with no further record of that prototype instance.
...
In some respects, the Spring container’s role in regard to a prototype-scoped bean is a replacement for the Java new operator. All lifecycle management past that point must be handled by the client.

Example of how you can convert the TypicalBean class to a prototype scoped bean:

@Component
@Scope("prototype")
public class TypicalBean {
   ...
}

Another alternative is to manually instantiate the bean class (or any POJO) and injecting the dependencies (configuration, spring beans, etc.) through the constructor or setter methods, if you have them available or can get them from the Spring Context.

new TypicalBean(properties);
Tore Brandtzæg
  • 586
  • 3
  • 5