Override ApplicationContext prepareRefresh() and onRefresh() methods.
Add option to copy configuration from the pre-refresh BeanFactory to the post-refresh BeanFactory instance. Apply appropriate Spring @NonNull and @Nullable annotations. Edit Javadoc.
This commit is contained in:
@@ -24,6 +24,8 @@ import java.util.Set;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
@@ -34,7 +36,10 @@ import org.springframework.context.annotation.AnnotationConfigUtils;
|
||||
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
|
||||
import org.springframework.context.annotation.ScopeMetadataResolver;
|
||||
import org.springframework.context.support.AbstractRefreshableConfigApplicationContext;
|
||||
import org.springframework.data.gemfire.config.annotation.PeerCacheApplication;
|
||||
import org.springframework.data.gemfire.util.ArrayUtils;
|
||||
import org.springframework.data.gemfire.util.SpringUtils;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -43,8 +48,17 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* A {@literal refreshable} {@link ApplicationContext} capable of loading {@link Class component classes} common
|
||||
* in {@link Annotation} based configuration as well as scanning {@link String configuration locations}.
|
||||
* A {@literal refreshable} {@link ApplicationContext} capable of loading {@link Class component classes} used for
|
||||
* {@link Annotation} based configuration in addition to scanning {@link String configuration locations}, and then
|
||||
* providing the ability to reload/refresh the context at some point later during runtime.
|
||||
*
|
||||
* DISCLAIMER: Currently, this {@link ApplicationContext} implementation (and extension) is being used exclusively for
|
||||
* testing and experimental (R&D) purposes. It was designed around Apache Geode & Pivotal GemFire's forced-disconnect
|
||||
* / auto-reconnect functionality, providing support for this behavior inside a Spring context. Specifically, this
|
||||
* concern is only applicable when using Spring Boot to configure and bootstrap Apache Geode or Pivotal GemFire peer
|
||||
* member {@link org.apache.geode.cache.Cache} applications, such as when annotating your Spring Boot application with
|
||||
* SDG's {@link PeerCacheApplication} annotation. This {@link ApplicationContext} implementation is not recommended for
|
||||
* use in Production Systems/Applications (yet).
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.springframework.beans.factory.config.BeanDefinition
|
||||
@@ -63,11 +77,13 @@ import org.slf4j.LoggerFactory;
|
||||
public class RefreshableAnnotationConfigApplicationContext extends AbstractRefreshableConfigApplicationContext
|
||||
implements AnnotationConfigRegistry {
|
||||
|
||||
protected static final boolean DEFAULT_COPY_CONFIGURATION = false;
|
||||
protected static final boolean USE_DEFAULT_FILTERS = true;
|
||||
|
||||
@Nullable
|
||||
private BeanNameGenerator beanNameGenerator;
|
||||
|
||||
@Nullable
|
||||
private volatile DefaultListableBeanFactory beanFactory;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
@@ -78,27 +94,39 @@ public class RefreshableAnnotationConfigApplicationContext extends AbstractRefre
|
||||
@Nullable
|
||||
private ScopeMetadataResolver scopeMetadataResolver;
|
||||
|
||||
// TODO: WARNING - Calling refreshBeanFactory() in the constructor to eagerly create a BeanFactory is problematic.
|
||||
// However, it does follow the BeanFactory creation pattern used by the GenericApplicationContext (and extensions
|
||||
// like AnnotationConfigApplicationContext) that this RefreshableAnnotationConfigApplicationContext implementation
|
||||
// is trying to preserve. Although, the AnnotationConfigApplicationContext implementation is NOT refreshable either,
|
||||
// yet the AnnotationConfigWebApplicationContext is but doesn't eagerly create a BeanFactory during construction,
|
||||
// so... The main problem with eagerly creating the BeanFactory in the constructor is the BeanFactory will be
|
||||
// closed and reconstructed on refresh, as well as on each refresh thereafter.
|
||||
|
||||
/**
|
||||
* Constructs an new instance of the {@link RefreshableAnnotationConfigApplicationContext}
|
||||
* with default container state an no {@literal parent} {@link ApplicationContext}.
|
||||
* with default container state and no {@literal parent} {@link ApplicationContext}.
|
||||
*
|
||||
* @see #refreshBeanFactory()
|
||||
* @see #RefreshableAnnotationConfigApplicationContext(ApplicationContext)
|
||||
*/
|
||||
public RefreshableAnnotationConfigApplicationContext() {
|
||||
refreshBeanFactory(); // TODO: Not certain this is a good idea; see comment below on createBeanFactory() method!
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new instance of the {@link RefreshableAnnotationConfigApplicationContext} initialized with
|
||||
* the {@literal parent} {@link ApplicationContext}.
|
||||
*
|
||||
* @param parent parent {@link ApplicationContext} to this context.
|
||||
* Additionally, this constructor eagerly initializes a {@link ConfigurableListableBeanFactory},
|
||||
* unlike {@link org.springframework.context.support.AbstractRefreshableApplicationContext} implementations,
|
||||
* but exactly like {@link org.springframework.context.support.GenericApplicationContext} implementations.
|
||||
*
|
||||
* @param parent parent {@link ApplicationContext} to this child context.
|
||||
* @see org.springframework.context.ApplicationContext
|
||||
* @see #refreshBeanFactory()
|
||||
*/
|
||||
public RefreshableAnnotationConfigApplicationContext(@Nullable ApplicationContext parent) {
|
||||
super(parent);
|
||||
refreshBeanFactory(); // TODO: Not certain this is a good idea; see comment below on createBeanFactory() method!
|
||||
refreshBeanFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -127,12 +155,12 @@ public class RefreshableAnnotationConfigApplicationContext extends AbstractRefre
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the configured {@link Logger} used to log framework {@link String messages} to the application log.
|
||||
* Returns the configured {@link Logger} used to log framework messages to the application log.
|
||||
*
|
||||
* @return the configured {@link Logger}.
|
||||
* @see org.slf4j.Logger
|
||||
*/
|
||||
protected Logger getLogger() {
|
||||
protected @NonNull Logger getLogger() {
|
||||
return this.logger;
|
||||
}
|
||||
|
||||
@@ -161,10 +189,26 @@ public class RefreshableAnnotationConfigApplicationContext extends AbstractRefre
|
||||
return Optional.ofNullable(this.scopeMetadataResolver);
|
||||
}
|
||||
|
||||
protected boolean isCopyConfigurationEnabled() {
|
||||
return DEFAULT_COPY_CONFIGURATION;
|
||||
}
|
||||
|
||||
protected boolean isUsingDefaultFilters() {
|
||||
return USE_DEFAULT_FILTERS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads {@link BeanDefinition BeanDefinitions} from Annotation configuration (component) classes
|
||||
* as well as from other resource locations (e.g. XML).
|
||||
*
|
||||
* @param beanFactory {@link DefaultListableBeanFactory} to configure.
|
||||
* @throws BeansException if loading and configuring the {@link BeanDefinition BeanDefintions} for the target
|
||||
* {@link DefaultListableBeanFactory} fails.
|
||||
* @see org.springframework.beans.factory.support.DefaultListableBeanFactory
|
||||
* @see #newAnnotatedBeanDefinitionReader(BeanDefinitionRegistry)
|
||||
* @see #newClassBeanDefinitionScanner(BeanDefinitionRegistry)
|
||||
* @see #getConfigLocations()
|
||||
*/
|
||||
@Override
|
||||
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException {
|
||||
|
||||
@@ -225,22 +269,6 @@ public class RefreshableAnnotationConfigApplicationContext extends AbstractRefre
|
||||
return scanner;
|
||||
}
|
||||
|
||||
// TODO: Not entirely certain this is a good idea (yet); However, it does follow the BeanFactory creation pattern
|
||||
// used by // the GenericApplicationContext (& extensions, e.g. AnnotationConfigApplicationContext) that I am
|
||||
// trying to // preserve in this RefreshableAnnotationConfigApplicationContext implementation. However, the
|
||||
// AnnotationConfigApplicationContext implementation is NOT refreshable either, although
|
||||
// AnnotationConfigWebApplicationContext is, so... hmm!
|
||||
// This overridden createBeanFactory() method effectively maintains the BeanFactory as a Singleton!
|
||||
@Override
|
||||
protected synchronized DefaultListableBeanFactory createBeanFactory() {
|
||||
|
||||
if (this.beanFactory == null) {
|
||||
this.beanFactory = super.createBeanFactory();
|
||||
}
|
||||
|
||||
return this.beanFactory;
|
||||
}
|
||||
|
||||
protected AnnotatedBeanDefinitionReader newAnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
|
||||
return new AnnotatedBeanDefinitionReader(registry, getEnvironment());
|
||||
}
|
||||
@@ -249,6 +277,47 @@ public class RefreshableAnnotationConfigApplicationContext extends AbstractRefre
|
||||
return new ClassPathBeanDefinitionScanner(registry, isUsingDefaultFilters(), getEnvironment());
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-registers Singleton beans registered with the previous {@link ConfigurableListableBeanFactory BeanFactory}
|
||||
* (prior to refresh) with this {@link ApplicationContext}, iff this context was previously active
|
||||
* and subsequently refreshed.
|
||||
*
|
||||
* @see org.springframework.beans.factory.config.ConfigurableListableBeanFactory#copyConfigurationFrom(ConfigurableBeanFactory)
|
||||
* @see org.springframework.beans.factory.config.ConfigurableListableBeanFactory#registerSingleton(String, Object)
|
||||
* @see #getBeanFactory()
|
||||
*/
|
||||
@Override
|
||||
protected void onRefresh() {
|
||||
|
||||
super.onRefresh();
|
||||
|
||||
ConfigurableListableBeanFactory currentBeanFactory = getBeanFactory();
|
||||
|
||||
if (this.beanFactory != null) {
|
||||
|
||||
Arrays.stream(ArrayUtils.nullSafeArray(this.beanFactory.getSingletonNames(), String.class))
|
||||
.filter(singletonBeanName -> !currentBeanFactory.containsSingleton(singletonBeanName))
|
||||
.forEach(singletonBeanName -> currentBeanFactory
|
||||
.registerSingleton(singletonBeanName, this.beanFactory.getSingleton(singletonBeanName)));
|
||||
|
||||
if (isCopyConfigurationEnabled()) {
|
||||
currentBeanFactory.copyConfigurationFrom(this.beanFactory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a reference to the previous {@link ConfigurableListableBeanFactory} in order to copy its configuration
|
||||
* and state on {@link ApplicationContext} refresh invocations.
|
||||
*
|
||||
* @see #getBeanFactory()
|
||||
*/
|
||||
@Override
|
||||
protected void prepareRefresh() {
|
||||
this.beanFactory = (DefaultListableBeanFactory) SpringUtils.safeGetValue(this::getBeanFactory);
|
||||
super.prepareRefresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user