spring-projects / spring-framework

Spring Framework
https://spring.io/projects/spring-framework
Apache License 2.0
56.38k stars 38.05k forks source link

Drop outdated BeanFactoryLocator / beanRefContext.xml mechanism [SPR-15154] #19720

Closed spring-projects-issues closed 7 years ago

spring-projects-issues commented 7 years ago

Juergen Hoeller opened SPR-15154 and commented

The BeanFactoryLocator facility and its beanRefContext.xml-based default variant are outdated mechanisms for locating a statically shared ApplicationContext, only really useful for old-school EAR deployment layouts prior to EJB 3.1. While it still makes sense for this to be available in the 4.3.x line with its EJB 3.0 support, let's get rid of the entire mechanism for Spring 5 with its EE 7 baseline.

This means that existing users of SpringBeanAutowiringInterceptor on custom EJB classes need to migrate either away from EJB altogether, or to an EJB 3.1 + CDI layer with a CDI-Spring bridge, or to a custom interceptor variant which locates the Spring ApplicationContext from an application-specific holder, e.g. an EJB 3.1 Singleton Bean or a simple static variable in an application class.


Issue Links:

spring-projects-issues commented 7 years ago

John Blum commented

Hi Juergen Hoeller-

In fact, the BeanFactoryLocator facility was used in more places than EJB-based, Java EE environments.

Spring Data GemFire (SDG) makes fairly extensive use of this facility to bridge the world of Spring config and GemFire's native cache.xml config. Costin Leau added this functionality in the very beginning of the SDG project, with the GemfireBeanFactoryLocator.

The idea is to let users auto-wire GemFire objects (e.g. CacheLoaders, CacheWriters, etc) defined/declared in GemFire's cache.xml with beans defined in the Spring ApplicationContext when cache.xml is still/also used.

While Spring is generally moving away from XML config, and while I have made several efforts to move SDG in the same direction, GemFire on the other hand is still largely configured with XML (i.e. cache.xml). Even GemFire's more modern approach to configuration, using the (relatively) new Cluster Configuration Service applied via commands executed in GemFire's Shell (Gfsh) tool, is backed by cache.xml (ironically).

As a result, this has led a few Pivotal customers (e.g. CDI, Citi, etc) to utilize a combination of both Spring config and GemFire config (whether that be with a node specific cache.xml file or by using the new Cluster Configuration Service) when configuring and adding a new, often times Spring (Boot) based, peer member (application) to the cluster (embedded UC).

Even in the more typical client/server scenarios, customers can use cache.xml to configure their GemFire cache client applications, which are typically Spring (Boot) applications. Using Spring Boot to create GemFire cache client applications will definitely be the norm in PCF.

Nearly all GemFire configuration can be expressed in Spring config (using SDG) largely because GemFire exposes an API for it. However, there are a few places, such as the GemFire to Greenplum Connector (G2C), that cannot be configured using Spring yet. Part of the reason for this is that GemFire does not expose appropriate configuration extensions in their API.

Long story short, robwinch caught this problem in the Spring Session build, which integrates with GemFire (as a Session Repository provider) via SDG. There will (possibly) be a few other Spring projects using SDG affected by this as well.

The good new is...

I think I have a solution to this (SDG) problem without having to propose re-introducing the BeanFactoryLocator back into Spring Framework 5.

Sometime ago (in SDG 1.4, actually), at the request of the Spring XD team at the time, I introduced the SpringContextBootstrappingInitializer along with the ability to lazy-wire GemFire objects declared in cache.xml (or even in Cluster Config; SDG doesn't care).

This was needed in UCs where GemFire bootstraps a Spring ApplicationContext in the runtime of a GemFire JVM process, which customers do as well. This is the exact inverse of what users typically do (i.e. having a Spring container bootstrap GemFire using SDG and Spring Boot).

So, I think I can repurpose this Initializer to do the work of the BeanFactoryLocator. However, I am very open to any alternate ideas or suggestions you may have.

Anyway, I just wanted to make you aware of this feature in SDG that was based on the BeanFactoryLocator.

Thanks for your time.

spring-projects-issues commented 7 years ago

Juergen Hoeller commented

John Blum, I wasn't aware of that; thanks for raising it.

From my perspective, BeanFactoryLocator is an outdated mechanism in several respects: Not only its typical use cases date back to ten years ago, its design is equally old and rather over-generalized. Also, the lifecycle of the contained factories isn't well-defined in such a static registry model with weak reference cleanup.

There are simpler ways to achieve the same, in particular if it doesn't have to be as overly flexible as BeanFactoryLocator was meant to be. I'd rather recommend a custom static holder class for one or more ApplicationContexts, keyed by something that has a meaning for your use case, and with a proper lifecycle.

spring-projects-issues commented 7 years ago

John Blum commented

Thank you for the quick response Juergen Hoeller.

I'd rather recommend a custom static holder class for one or more ApplicationContexts, keyed by something that has a meaning for your use case, and with a proper lifecycle.

Sounds reasonable and kind of similar to how Costin originally implemented the GemfireBeanFactoryLocator.

Additionally, as I mentioned, I introduced the SpringContextBootstrappingInitializer for the lazy-wiring, GemFire bootstrapping Spring UC, that keeps a static holder to a ApplicationContext. It could probably due with some more work around proper lifecycle handling, but then it's original intent was tied to the lifecycle of GemFire.

Anyway, I am currently trying to build on this and replace all uses of GemfireBeanFactoryLocator.

I will post back here if I have anymore related questions since others might have similar problems and would look for ways on how to replace their BeanFactoryLocator implementation behavior.

Thanks again.

spring-projects-issues commented 7 years ago

Michael Minella commented

Sorry to be commenting on a resolved issue, but why was this never depricated and just dropped without any formal guidance as to what to do in it's place? For the record, Batch is using this as well to bootstrap the infrastructure required for JSR-352.

spring-projects-issues commented 7 years ago

Juergen Hoeller commented

Michael Minella, commenting here is fine, this is not released yet after all: It's waiting for 5.0 M5 in February.

The reason for BeanFactoryLocator not having been deprecated is that we're still considering WebSphere 7 and EJB 3.0 environments in general as a first-class citizen in the Spring Framework 4.3.x line. It would be odd to have to recommend a deprecated mechanism there, even if it effectively was deprecated for any other usage already.

Now, as of Spring Framework 5.0 with its EE 7 baseline, there is no supported EE environment left where the BeanFactoryLocator mechanism is actually useful. I admit that it was a bit harsh to just remove it, but hey, otherwise we wouldn't even have this discussion ;-) In my obversation, deprecating leads to almost no feedback, but hard CI build failures obviously trigger it indeed. And in 5.0, we generally opt for removal over deprecation, trying to create a clean plate for the entire 5.x generation.

What lifecycle have you been using BeanFactoryLocator with? Could you replace it with direct ClassPathXmlApplicationContext setup, managing the ApplicationContext in a custom static field of yours, and also shutting it down at a well-defined point? Any such option would have a clearer lifecycle than BeanFactoryLocator's over-generalized static registry of factories which was only really designed for environments where no other lifecycle can be enforced.

spring-projects-issues commented 7 years ago

Michael Minella commented

Juergen Hoeller, Thanks for the quick response. Let me explain the use case I'm using it for and maybe you can guide me down a better path (or at least help me see the path I should be on).

Per JSR-352, the way jobs are launched is via the following code:

JobOperator jobOperator = BatchRuntime.getJobOperator();
jobOperator.start("myJobName", null);

BatchRuntime is a class provided by the specification so I cannot customize that. It uses a ServiceLoader to load a JobOperator instance (our JsrJobOperator in this case) and return it so I'm limited as to how I can customize the building of the JobOperator in the first place.

Currently, in the no-arg constructor for the JobOperator, I have the following:

BeanFactoryLocator beanFactoryLocactor = ContextSingletonBeanFactoryLocator.getInstance();
BeanFactoryReference ref = beanFactoryLocactor.useBeanFactory("baseContext");
baseContext = (ApplicationContext) ref.getFactory();

This code allows me to bootstrap a new base context if one hasn't been created and reference it if it has. That base context contains all of the shared batch infrastructure components (JobRepository, etc) and serves as the parent context for any of the job specific ApplicationContext that are boostrapped when an actual job is run. As for shutdown, there are no well defined mechanisms within the JSR around that so JVM shutdown is the only sensible option as to when to close the context.

Is the right way going forward for me to handle what the ContextSingletonBeanFactoryLocator was accomplishing via a static reference to a context in the JsrJobOperator going forward (old school singleton style)? Again, any insight you can provide is appreciated.

spring-projects-issues commented 7 years ago

Michael Minella commented

Juergen Hoeller, I wanted to give this a bump. I have downgraded the version of Spring Framework to 4.3.6 in Batch 4 because our build breaks without a solution to this. I'd like to get this addressed before my next milestone which I'm targeting for before DevNexus (in a couple weeks). Any insight you can provide is appreciated. Thanks in advance!

spring-projects-issues commented 7 years ago

John Blum commented

Hi Michael Minella-

Spring Data GemFire had similar problems, as I described above.

In the end, my solution was to keep yet simplify as much of the existing behavior, based on the original BeanFactoryLocator concepts, as possible. But, it really is as Juergen Hoeller recommended...

... a custom static holder class for one or more ApplicationContexts, keyed by something that has a meaning for your use case, and with a proper lifecycle.

To see how I implemented this for Spring Data GemFire see the GemfireBeanFactoryLocator class before and after.

While this may or may not work for Spring Batch, hopefully it will give you some ideas.

Cheers, John Blum

ramya22 commented 4 years ago

I'm also facing similar issue while migrating to Spring 5.x. What all the possible solutions for this ? we are using EJB2

MarcinCieslak commented 2 years ago

If anyone still needs this, here is how we reintroduced the locator mechanism into our code.

Staticsubh commented 2 years ago

I am still not able to upgrade from Spring 4 to Spring 5 because of this issue only. Please go through the issue #28140 . Just for the information I have made changes as mentioned in the above comment by @MarcinCieslak