All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.context.support.GenericApplicationContext Maven / Gradle / Ivy

/*
 * Copyright 2002-2022 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.context.support;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;

import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.support.ClassHintUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionCustomizer;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.ProtocolResolver;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.metrics.ApplicationStartup;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

/**
 * Generic ApplicationContext implementation that holds a single internal
 * {@link org.springframework.beans.factory.support.DefaultListableBeanFactory}
 * instance and does not assume a specific bean definition format. Implements
 * the {@link org.springframework.beans.factory.support.BeanDefinitionRegistry}
 * interface in order to allow for applying any bean definition readers to it.
 *
 * 

Typical usage is to register a variety of bean definitions via the * {@link org.springframework.beans.factory.support.BeanDefinitionRegistry} * interface and then call {@link #refresh()} to initialize those beans * with application context semantics (handling * {@link org.springframework.context.ApplicationContextAware}, auto-detecting * {@link org.springframework.beans.factory.config.BeanFactoryPostProcessor BeanFactoryPostProcessors}, * etc). * *

In contrast to other ApplicationContext implementations that create a new * internal BeanFactory instance for each refresh, the internal BeanFactory of * this context is available right from the start, to be able to register bean * definitions on it. {@link #refresh()} may only be called once. * *

This ApplicationContext implementation is suitable for Ahead of Time * processing, using {@link #refreshForAotProcessing} as an alternative to the * regular {@link #refresh()}. * *

Usage example: * *

 * GenericApplicationContext ctx = new GenericApplicationContext();
 * XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(ctx);
 * xmlReader.loadBeanDefinitions(new ClassPathResource("applicationContext.xml"));
 * PropertiesBeanDefinitionReader propReader = new PropertiesBeanDefinitionReader(ctx);
 * propReader.loadBeanDefinitions(new ClassPathResource("otherBeans.properties"));
 * ctx.refresh();
 *
 * MyBean myBean = (MyBean) ctx.getBean("myBean");
 * ...
* * For the typical case of XML bean definitions, you may also use * {@link ClassPathXmlApplicationContext} or {@link FileSystemXmlApplicationContext}, * which are easier to set up - but less flexible, since you can just use standard * resource locations for XML bean definitions, rather than mixing arbitrary bean * definition formats. For a custom application context implementation supposed to * read a specific bean definition format in a refreshable manner, consider * deriving from the {@link AbstractRefreshableApplicationContext} base class. * * @author Juergen Hoeller * @author Chris Beams * @author Stephane Nicoll * @author Sam Brannen * @since 1.1.2 * @see #registerBeanDefinition * @see #refresh() * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader * @see org.springframework.beans.factory.support.PropertiesBeanDefinitionReader */ public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry { private final DefaultListableBeanFactory beanFactory; @Nullable private ResourceLoader resourceLoader; private boolean customClassLoader = false; private final AtomicBoolean refreshed = new AtomicBoolean(); /** * Create a new GenericApplicationContext. * @see #registerBeanDefinition * @see #refresh */ public GenericApplicationContext() { this.beanFactory = new DefaultListableBeanFactory(); } /** * Create a new GenericApplicationContext with the given DefaultListableBeanFactory. * @param beanFactory the DefaultListableBeanFactory instance to use for this context * @see #registerBeanDefinition * @see #refresh */ public GenericApplicationContext(DefaultListableBeanFactory beanFactory) { Assert.notNull(beanFactory, "BeanFactory must not be null"); this.beanFactory = beanFactory; } /** * Create a new GenericApplicationContext with the given parent. * @param parent the parent application context * @see #registerBeanDefinition * @see #refresh */ public GenericApplicationContext(@Nullable ApplicationContext parent) { this(); setParent(parent); } /** * Create a new GenericApplicationContext with the given DefaultListableBeanFactory. * @param beanFactory the DefaultListableBeanFactory instance to use for this context * @param parent the parent application context * @see #registerBeanDefinition * @see #refresh */ public GenericApplicationContext(DefaultListableBeanFactory beanFactory, ApplicationContext parent) { this(beanFactory); setParent(parent); } /** * Set the parent of this application context, also setting * the parent of the internal BeanFactory accordingly. * @see org.springframework.beans.factory.config.ConfigurableBeanFactory#setParentBeanFactory */ @Override public void setParent(@Nullable ApplicationContext parent) { super.setParent(parent); this.beanFactory.setParentBeanFactory(getInternalParentBeanFactory()); } @Override public void setApplicationStartup(ApplicationStartup applicationStartup) { super.setApplicationStartup(applicationStartup); this.beanFactory.setApplicationStartup(applicationStartup); } /** * Set whether it should be allowed to override bean definitions by registering * a different definition with the same name, automatically replacing the former. * If not, an exception will be thrown. Default is "true". * @since 3.0 * @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowBeanDefinitionOverriding */ public void setAllowBeanDefinitionOverriding(boolean allowBeanDefinitionOverriding) { this.beanFactory.setAllowBeanDefinitionOverriding(allowBeanDefinitionOverriding); } /** * Set whether to allow circular references between beans - and automatically * try to resolve them. *

Default is "true". Turn this off to throw an exception when encountering * a circular reference, disallowing them completely. * @since 3.0 * @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowCircularReferences */ public void setAllowCircularReferences(boolean allowCircularReferences) { this.beanFactory.setAllowCircularReferences(allowCircularReferences); } /** * Set a ResourceLoader to use for this context. If set, the context will * delegate all {@code getResource} calls to the given ResourceLoader. * If not set, default resource loading will apply. *

The main reason to specify a custom ResourceLoader is to resolve * resource paths (without URL prefix) in a specific fashion. * The default behavior is to resolve such paths as class path locations. * To resolve resource paths as file system locations, specify a * FileSystemResourceLoader here. *

You can also pass in a full ResourcePatternResolver, which will * be autodetected by the context and used for {@code getResources} * calls as well. Else, default resource pattern matching will apply. * @see #getResource * @see org.springframework.core.io.DefaultResourceLoader * @see org.springframework.core.io.FileSystemResourceLoader * @see org.springframework.core.io.support.ResourcePatternResolver * @see #getResources */ public void setResourceLoader(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; } //--------------------------------------------------------------------- // ResourceLoader / ResourcePatternResolver override if necessary //--------------------------------------------------------------------- /** * This implementation delegates to this context's {@code ResourceLoader} if set, * falling back to the default superclass behavior otherwise. *

As of Spring Framework 5.3.22, this method also honors registered * {@linkplain #getProtocolResolvers() protocol resolvers} when a custom * {@code ResourceLoader} has been set. * @see #setResourceLoader(ResourceLoader) * @see #addProtocolResolver(ProtocolResolver) */ @Override public Resource getResource(String location) { if (this.resourceLoader != null) { for (ProtocolResolver protocolResolver : getProtocolResolvers()) { Resource resource = protocolResolver.resolve(location, this); if (resource != null) { return resource; } } return this.resourceLoader.getResource(location); } return super.getResource(location); } /** * This implementation delegates to this context's ResourceLoader if it * implements the ResourcePatternResolver interface, falling back to the * default superclass behavior otherwise. * @see #setResourceLoader */ @Override public Resource[] getResources(String locationPattern) throws IOException { if (this.resourceLoader instanceof ResourcePatternResolver resourcePatternResolver) { return resourcePatternResolver.getResources(locationPattern); } return super.getResources(locationPattern); } @Override public void setClassLoader(@Nullable ClassLoader classLoader) { super.setClassLoader(classLoader); this.customClassLoader = true; } @Override @Nullable public ClassLoader getClassLoader() { if (this.resourceLoader != null && !this.customClassLoader) { return this.resourceLoader.getClassLoader(); } return super.getClassLoader(); } //--------------------------------------------------------------------- // Implementations of AbstractApplicationContext's template methods //--------------------------------------------------------------------- /** * Do nothing: We hold a single internal BeanFactory and rely on callers * to register beans through our public methods (or the BeanFactory's). * @see #registerBeanDefinition */ @Override protected final void refreshBeanFactory() throws IllegalStateException { if (!this.refreshed.compareAndSet(false, true)) { throw new IllegalStateException( "GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once"); } this.beanFactory.setSerializationId(getId()); } @Override protected void cancelRefresh(BeansException ex) { this.beanFactory.setSerializationId(null); super.cancelRefresh(ex); } /** * Not much to do: We hold a single internal BeanFactory that will never * get released. */ @Override protected final void closeBeanFactory() { this.beanFactory.setSerializationId(null); } /** * Return the single internal BeanFactory held by this context * (as ConfigurableListableBeanFactory). */ @Override public final ConfigurableListableBeanFactory getBeanFactory() { return this.beanFactory; } /** * Return the underlying bean factory of this context, * available for registering bean definitions. *

NOTE: You need to call {@link #refresh()} to initialize the * bean factory and its contained beans with application context semantics * (autodetecting BeanFactoryPostProcessors, etc). * @return the internal bean factory (as DefaultListableBeanFactory) */ public final DefaultListableBeanFactory getDefaultListableBeanFactory() { return this.beanFactory; } @Override public AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException { assertBeanFactoryActive(); return this.beanFactory; } //--------------------------------------------------------------------- // Implementation of BeanDefinitionRegistry //--------------------------------------------------------------------- @Override public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { this.beanFactory.registerBeanDefinition(beanName, beanDefinition); } @Override public void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException { this.beanFactory.removeBeanDefinition(beanName); } @Override public BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException { return this.beanFactory.getBeanDefinition(beanName); } @Override public boolean isBeanNameInUse(String beanName) { return this.beanFactory.isBeanNameInUse(beanName); } @Override public void registerAlias(String beanName, String alias) { this.beanFactory.registerAlias(beanName, alias); } @Override public void removeAlias(String alias) { this.beanFactory.removeAlias(alias); } @Override public boolean isAlias(String beanName) { return this.beanFactory.isAlias(beanName); } //--------------------------------------------------------------------- // AOT processing //--------------------------------------------------------------------- /** * Load or refresh the persistent representation of the configuration up to * a point where the underlying bean factory is ready to create bean * instances. *

This variant of {@link #refresh()} is used by Ahead of Time (AOT) * processing that optimizes the application context, typically at build time. *

In this mode, only {@link BeanDefinitionRegistryPostProcessor} and * {@link MergedBeanDefinitionPostProcessor} are invoked. * @param runtimeHints the runtime hints * @throws BeansException if the bean factory could not be initialized * @throws IllegalStateException if already initialized and multiple refresh * attempts are not supported * @since 6.0 */ public void refreshForAotProcessing(RuntimeHints runtimeHints) { if (logger.isDebugEnabled()) { logger.debug("Preparing bean factory for AOT processing"); } prepareRefresh(); obtainFreshBeanFactory(); prepareBeanFactory(this.beanFactory); postProcessBeanFactory(this.beanFactory); invokeBeanFactoryPostProcessors(this.beanFactory); this.beanFactory.freezeConfiguration(); PostProcessorRegistrationDelegate.invokeMergedBeanDefinitionPostProcessors(this.beanFactory); preDetermineBeanTypes(runtimeHints); } /** * Pre-determine bean types in order to trigger early proxy class creation. * @see org.springframework.beans.factory.BeanFactory#getType * @see SmartInstantiationAwareBeanPostProcessor#determineBeanType */ private void preDetermineBeanTypes(RuntimeHints runtimeHints) { List bpps = PostProcessorRegistrationDelegate.loadBeanPostProcessors( this.beanFactory, SmartInstantiationAwareBeanPostProcessor.class); for (String beanName : this.beanFactory.getBeanDefinitionNames()) { Class beanType = this.beanFactory.getType(beanName); if (beanType != null) { ClassHintUtils.registerProxyIfNecessary(beanType, runtimeHints); for (SmartInstantiationAwareBeanPostProcessor bpp : bpps) { Class newBeanType = bpp.determineBeanType(beanType, beanName); if (newBeanType != beanType) { ClassHintUtils.registerProxyIfNecessary(newBeanType, runtimeHints); beanType = newBeanType; } } } } } //--------------------------------------------------------------------- // Convenient methods for registering individual beans //--------------------------------------------------------------------- /** * Register a bean from the given bean class, optionally providing explicit * constructor arguments for consideration in the autowiring process. * @param beanClass the class of the bean * @param constructorArgs custom argument values to be fed into Spring's * constructor resolution algorithm, resolving either all arguments or just * specific ones, with the rest to be resolved through regular autowiring * (may be {@code null} or empty) * @since 5.2 (since 5.0 on the AnnotationConfigApplicationContext subclass) */ public void registerBean(Class beanClass, Object... constructorArgs) { registerBean(null, beanClass, constructorArgs); } /** * Register a bean from the given bean class, optionally providing explicit * constructor arguments for consideration in the autowiring process. * @param beanName the name of the bean (may be {@code null}) * @param beanClass the class of the bean * @param constructorArgs custom argument values to be fed into Spring's * constructor resolution algorithm, resolving either all arguments or just * specific ones, with the rest to be resolved through regular autowiring * (may be {@code null} or empty) * @since 5.2 (since 5.0 on the AnnotationConfigApplicationContext subclass) */ public void registerBean(@Nullable String beanName, Class beanClass, Object... constructorArgs) { registerBean(beanName, beanClass, (Supplier) null, bd -> { for (Object arg : constructorArgs) { bd.getConstructorArgumentValues().addGenericArgumentValue(arg); } }); } /** * Register a bean from the given bean class, optionally customizing its * bean definition metadata (typically declared as a lambda expression). * @param beanClass the class of the bean (resolving a public constructor * to be autowired, possibly simply the default constructor) * @param customizers one or more callbacks for customizing the factory's * {@link BeanDefinition}, e.g. setting a lazy-init or primary flag * @since 5.0 * @see #registerBean(String, Class, Supplier, BeanDefinitionCustomizer...) */ public final void registerBean(Class beanClass, BeanDefinitionCustomizer... customizers) { registerBean(null, beanClass, null, customizers); } /** * Register a bean from the given bean class, optionally customizing its * bean definition metadata (typically declared as a lambda expression). * @param beanName the name of the bean (may be {@code null}) * @param beanClass the class of the bean (resolving a public constructor * to be autowired, possibly simply the default constructor) * @param customizers one or more callbacks for customizing the factory's * {@link BeanDefinition}, e.g. setting a lazy-init or primary flag * @since 5.0 * @see #registerBean(String, Class, Supplier, BeanDefinitionCustomizer...) */ public final void registerBean( @Nullable String beanName, Class beanClass, BeanDefinitionCustomizer... customizers) { registerBean(beanName, beanClass, null, customizers); } /** * Register a bean from the given bean class, using the given supplier for * obtaining a new instance (typically declared as a lambda expression or * method reference), optionally customizing its bean definition metadata * (again typically declared as a lambda expression). * @param beanClass the class of the bean * @param supplier a callback for creating an instance of the bean * @param customizers one or more callbacks for customizing the factory's * {@link BeanDefinition}, e.g. setting a lazy-init or primary flag * @since 5.0 * @see #registerBean(String, Class, Supplier, BeanDefinitionCustomizer...) */ public final void registerBean( Class beanClass, Supplier supplier, BeanDefinitionCustomizer... customizers) { registerBean(null, beanClass, supplier, customizers); } /** * Register a bean from the given bean class, using the given supplier for * obtaining a new instance (typically declared as a lambda expression or * method reference), optionally customizing its bean definition metadata * (again typically declared as a lambda expression). *

This method can be overridden to adapt the registration mechanism for * all {@code registerBean} methods (since they all delegate to this one). * @param beanName the name of the bean (may be {@code null}) * @param beanClass the class of the bean * @param supplier a callback for creating an instance of the bean (in case * of {@code null}, resolving a public constructor to be autowired instead) * @param customizers one or more callbacks for customizing the factory's * {@link BeanDefinition}, e.g. setting a lazy-init or primary flag * @since 5.0 */ public void registerBean(@Nullable String beanName, Class beanClass, @Nullable Supplier supplier, BeanDefinitionCustomizer... customizers) { ClassDerivedBeanDefinition beanDefinition = new ClassDerivedBeanDefinition(beanClass); if (supplier != null) { beanDefinition.setInstanceSupplier(supplier); } for (BeanDefinitionCustomizer customizer : customizers) { customizer.customize(beanDefinition); } String nameToUse = (beanName != null ? beanName : beanClass.getName()); registerBeanDefinition(nameToUse, beanDefinition); } /** * {@link RootBeanDefinition} marker subclass for {@code #registerBean} based * registrations with flexible autowiring for public constructors. */ @SuppressWarnings("serial") private static class ClassDerivedBeanDefinition extends RootBeanDefinition { public ClassDerivedBeanDefinition(Class beanClass) { super(beanClass); } public ClassDerivedBeanDefinition(ClassDerivedBeanDefinition original) { super(original); } @Override @Nullable public Constructor[] getPreferredConstructors() { Class clazz = getBeanClass(); Constructor primaryCtor = BeanUtils.findPrimaryConstructor(clazz); if (primaryCtor != null) { return new Constructor[] {primaryCtor}; } Constructor[] publicCtors = clazz.getConstructors(); if (publicCtors.length > 0) { return publicCtors; } return null; } @Override public RootBeanDefinition cloneBeanDefinition() { return new ClassDerivedBeanDefinition(this); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy