Crystal Paladin opened SPR-15767 and commented

Whenever a Prototype scoped Bean is marked with an @Autowired Environment(Spring framework provided bean), and that prototype scoped Bean is instantiated by JSR330 Provider, and the Class which has the Provider implements a Runnable interface and a thread is created from the program execution point main method, we run into an issue where the Environment bean is not injected at all and results in NoSuchBeanDefinitionException: No bean named 'environment' available

Spring Creates the Environment Object as well as the Environment Bean Defined in the Java Config Classes... But When Using a thread inside a try with resources block, Spring Destroys the Environment Object that it created but not the One It created from the Java Config Classes when the try with resource block reaches the end...

@Configuration
@ComponentScan
public class ClassConfig {
    @Bean
    public Environment environment(ApplicationContext context) {
        return context.getEnvironment();
    }
}

but if we Autowire the Environment in ClassC (which is the threaded bean), the injection works fine...

but if we try to Autowire the Environment in ClassB (which is at the third level injected at a prototype class ClassA), We get the injection error 'environment' bean not found

i'm not sure if this is a Spring Issue or a JSR 330 issue since the Provider\ is used for the prototype class...

Note: Please Refer the Attached Sample Java Source files for complete analysis


Affects: 4.3.9

Reference URL: https://stackoverflow.com/questions/45076679/threaded-beans-doesnt-get-environment-autowired-to-them-in-spring-when-using-j

Attachments: - ClassA.java (622 bytes) - ClassB.java (570 bytes) - ClassC.java (841 bytes) - ClassConfig.java (207 bytes) - DummyParent.java (186 bytes) - InterA.java (73 bytes) - InterB.java (75 bytes) - SpringIssueReplicator.java (489 bytes)

Comment From: spring-projects-issues

Crystal Paladin commented

simple java config file

Comment From: spring-projects-issues

Crystal Paladin commented

Although the Environment Bean is not injected Automatically, we can implement a workaround in the java config file and include the following to make it work...

@Bean
public Environment environment(ApplicationContext context) {
    return context.getEnvironment();
}

Comment From: spring-projects-issues

Juergen Hoeller commented

The root of the problem is that you're closing your ApplicationContext through the try-with-resources clause here: This is effectively the same as an explicit close() call right after obtaining your bean reference. After closing, none of the context's resources are guaranteed to be available anymore since they've been cleaned up at that point; this includes the associated Environment. For that reason, your test case not only fails with asynchronous execution in a different thread; it also fails with a straight obj.run() call right after the try-with-resources.

Consider your ApplicationContext as a stateful resource with lifecycle-managed components, not just as a factory. You'll have to keep the context open as long as you're using some of its resources, including instance providers.

I'm repurposing this issue towards better error reporting here. The exception should really indicate that the context has been closed at that point, like it does for other access operations already... but not for cached autowired references yet.

Comment From: spring-projects-issues

Crystal Paladin commented

Is there a specific reason that spring doesn't destroy(during ApplicationContext close()) the beans auto created on java config classes?

Comment From: spring-projects-issues

Crystal Paladin commented

Is it possible to empower the JSR330 Provider to inject Environment (lazily during runtime) even if ApplicationContext is closed?

Comment From: spring-projects-issues

Juergen Hoeller commented

An ApplicationContext effectively destroys all beans on close: by calling destroy methods on them, and by letting go of their instances eventually. However, if your bean doesn't have a destroy method declared (e.g. through implementing DisposableBean or (Auto)Closeable), the instance won't notice that it is already inactive from the context perspective (and that the context is closed).

In any case, once the ApplicationContext is closed, fresh resolution of dependencies is not going to work anymore - by definition. You may keep using injected references (in case of a regular non-lazy Environment injection point) but you really shouldn't bend this so far. Instead, make sure that your ApplicationContext remains active as long as you need it, i.e. don't prematurely close it.