【SpringIOC容器设计体系分析】

SpringIoc容器架构分析

分析Spring的容器体系,以目前使用较为广泛的AnnotationConfigApplicationContext为例来分析,类继承体系图如下:

BeanFactory

顶层的BeanFactory接口仅提供最基本的获取bean的方法,以及获取bean的provide(bean所在容器对象)的方法。

getBean系列方法:获取bean对象

getBeanProvider:获取bean所在的容器

containsBean:bean是否存在

isSingleton:bean是否是单例

isPrototype:bean是否是原型bean

isTypeMatch:给定的bean是否匹配指定的类型

getType:获取bean的类型

getAliases:获取bean的别名列表

HierarchicalBeanFactory

层次化BeanFactory,从它的名字可知,他提供的核心是层次,层次就是分层,容器分层的话就是有父子容器,这个接口很简单,就是提供两个接口:

java 复制代码
public interface HierarchicalBeanFactory extends BeanFactory {

	/**
	 * Return the parent bean factory, or {@code null} if there is none.
	 */
	@Nullable
	BeanFactory getParentBeanFactory();

	/**
	 * Return whether the local bean factory contains a bean of the given name,
	 * ignoring beans defined in ancestor contexts.
	 * <p>This is an alternative to {@code containsBean}, ignoring a bean
	 * of the given name from an ancestor bean factory.
	 * @param name the name of the bean to query
	 * @return whether a bean with the given name is defined in the local factory
	 * @see BeanFactory#containsBean
	 */
	boolean containsLocalBean(String name);

}

ListableBeanFactory

可列举的bean,从名字可以知道它的主要特点是:可列举。可列举是什么意思,就是它提供接口可以一次性获取到这个容器内部的所有bean信息,而不是像BeanFactory一样需要通过getBean一个一个获取bean。这里需要注意,如果当前容器同时是ListableBeanFactory和HierarchicalBeanFactory,那么在查找bean的过程中不会去父容器中查找。

containsBeanDefinition:是否包含指定bean名称的bean元信息

String[] getBeanDefinitionNames(); 获取所有bean的名字。

getBeanProvider系列方法:获取bean的容器

String[] getBeanNamesForType(ResolvableType type);返回满足给定bean类型的所有bean名字。

Map<String, T> getBeansOfType(@Nullable Class type) throws BeansException; 返回指定类型的bean

String[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType);获取包含指定注解的所有bean名字。

findAnnotationOnBean:给定bean名字,查它的注解

A findAnnotationOnBean(

String beanName, Class annotationType, boolean allowFactoryBeanInit)

throws NoSuchBeanDefinitionException;

总结:提供列举bean的接口,提供获取bean注解的接口。

AutowireCapableBeanFactory

继承自BeanFactory,提供组件的自动注入功能。可以为一些现有的没有被spring管理的bean提供自动注入功能,比如自定义的组件依赖spring管理的bean,可借助此接口提供的方法完成注入。比如,如果需要给自定义的Servlet等没有被SpringFramework统一管理的bean注入组件,可以在获取AutowriteCapableBeanFactory之后调用其API实现依赖注入。

这个接口定义了创建bean的方法、运行beanPostProcess的方法,resolveBean等方法,主要功能就是完成bean的创建及依赖注入。

java 复制代码
<T> T createBean(Class<T> beanClass) throws BeansException;
void autowireBean(Object existingBean) throws BeansException;
Object configureBean(Object existingBean, String beanName) throws BeansException;
void applyBeanPropertyValues(Object existingBean, String beanName) throws BeansException;
Object initializeBean(Object existingBean, String beanName) throws BeansException;
Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
			throws BeansException;
Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
			throws BeansException;
void destroyBean(Object existingBean);
<T> NamedBeanHolder<T> resolveNamedBean(Class<T> requiredType) throws BeansException;

#ConfigurableBeanFactory

继承关系如下:

java 复制代码
public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, SingletonBeanRegistry

这个接口的功能如其名(Configurable),主要提供带配置的功能,可以调用它定义的方法对BeanFactory进行修改扩展等操作。但是官方不建议开发者去调用这个接口的方法,因为原则上程序在运行期间对BeanFactory进行频繁地改动不好,此时应该只有读的操作。其提供的主要方法(主要例句set等修改类方法)如下:

java 复制代码
void setParentBeanFactory(BeanFactory parentBeanFactory) throws IllegalStateException;
void setBeanClassLoader(@Nullable ClassLoader beanClassLoader);
void setTempClassLoader(@Nullable ClassLoader tempClassLoader);
void setCacheBeanMetadata(boolean cacheBeanMetadata);
void setBeanExpressionResolver(@Nullable BeanExpressionResolver resolver);
void setConversionService(@Nullable ConversionService conversionService);
void addPropertyEditorRegistrar(PropertyEditorRegistrar registrar);
void registerCustomEditor(Class<?> requiredType, Class<? extends PropertyEditor> propertyEditorClass);
void setTypeConverter(TypeConverter typeConverter);
void addEmbeddedValueResolver(StringValueResolver valueResolver);
void addBeanPostProcessor(BeanPostProcessor beanPostProcessor);
void registerScope(String scopeName, Scope scope);
void registerDependentBean(String beanName, String dependentBeanName);
void destroyBean(String beanName, Object beanInstance);

AbstractBeanFactory

它是BeanFactory最基础的抽象实现,仅有部分功能。

1、 能够从配置源(XML、LDAP、RDBMS)获取Bean的定义信息BeanDefinition。

2、 提供单实例Bean的缓存,单实例/原型Bean的裁定FactoryBean处理,Bean对象别名存储、用于子Bean定义BeanDefinition合并以及Bean销毁。

3、模板方法getBeanDefinition和createBean规范了Bean定义读取和创建bean的过程,子类实现具体的业务逻辑。

实现了getBean方法,里面会实际创建bean,在doGetBean方法中实现了bean创建基本流程规范。

提供了判断bean的scope的实现(是否单例或者原型)。

实现isTypeMacth方法

实现getType方法

实现getAliases方法

实现get、setParenBeanFactory方法

实现addBeanPostProcessor方法

AbstractAutowireCapableBeanFactory

继承关系如下:

java 复制代码
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
		implements AutowireCapableBeanFactory

实现了默认bean对象创建逻辑的BeanFactory接口的抽象类,除了实现父类AbstractBeanFactory的createBean方法,还实现了AutowireCapableBeanFactory接口的方法,即Bean对象真正的创建动作都在此实现。

实现bean对象的创建、属性的赋值和依赖注入以及Bean的初始化逻辑执行。

子类需要实现resolveDependency方法,解析Bean的成员中定义的属性依赖关系,由子类DefaultListableBeanFactory实现。

这个类主要实现的是Bean创建,而BeanDefinition的读取解析工作没在这里实现。

DefaultListableBeanFactory

类继承关系如下:

java 复制代码
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
		implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable

是一个成熟的落地实现,其还实现了BeanDefinitionRegistry接口,完成对BeanDefinition的读取功能,即完成Bean定义信息加载。

一般地,首先需要先注册Bean的定义信息,再完成Bean的创建和初始化动作,这个类就实现了这个逻辑。

需要注意的是,此类不实现特定Bean信息的读取和解析,而是通过委托的方式,让其他BeanDefinitionReader实现。

BeanDefinitionRegistry定义如下:

java 复制代码
/**
 * Interface for registries that hold bean definitions, for example RootBeanDefinition
 * and ChildBeanDefinition instances. Typically implemented by BeanFactories that
 * internally work with the AbstractBeanDefinition hierarchy.
 *
 * <p>This is the only interface in Spring's bean factory packages that encapsulates
 * <i>registration</i> of bean definitions. The standard BeanFactory interfaces
 * only cover access to a <i>fully configured factory instance</i>.
 *
 * <p>Spring's bean definition readers expect to work on an implementation of this
 * interface. Known implementors within the Spring core are DefaultListableBeanFactory
 * and GenericApplicationContext.
 *
 * @author Juergen Hoeller
 * @since 26.11.2003
 * @see org.springframework.beans.factory.config.BeanDefinition
 * @see AbstractBeanDefinition
 * @see RootBeanDefinition
 * @see ChildBeanDefinition
 * @see DefaultListableBeanFactory
 * @see org.springframework.context.support.GenericApplicationContext
 * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
 * @see PropertiesBeanDefinitionReader
 */
public interface BeanDefinitionRegistry extends AliasRegistry {

	/**
	 * Register a new bean definition with this registry.
	 * Must support RootBeanDefinition and ChildBeanDefinition.
	 * @param beanName the name of the bean instance to register
	 * @param beanDefinition definition of the bean instance to register
	 * @throws BeanDefinitionStoreException if the BeanDefinition is invalid
	 * @throws BeanDefinitionOverrideException if there is already a BeanDefinition
	 * for the specified bean name and we are not allowed to override it
	 * @see GenericBeanDefinition
	 * @see RootBeanDefinition
	 * @see ChildBeanDefinition
	 */
	void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException;

	/**
	 * Remove the BeanDefinition for the given name.
	 * @param beanName the name of the bean instance to register
	 * @throws NoSuchBeanDefinitionException if there is no such bean definition
	 */
	void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;

	/**
	 * Return the BeanDefinition for the given bean name.
	 * @param beanName name of the bean to find a definition for
	 * @return the BeanDefinition for the given name (never {@code null})
	 * @throws NoSuchBeanDefinitionException if there is no such bean definition
	 */
	BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;

	/**
	 * Check if this registry contains a bean definition with the given name.
	 * @param beanName the name of the bean to look for
	 * @return if this registry contains a bean definition with the given name
	 */
	boolean containsBeanDefinition(String beanName);

	/**
	 * Return the names of all beans defined in this registry.
	 * @return the names of all beans defined in this registry,
	 * or an empty array if none defined
	 */
	String[] getBeanDefinitionNames();

	/**
	 * Return the number of beans defined in the registry.
	 * @return the number of beans defined in the registry
	 */
	int getBeanDefinitionCount();

	/**
	 * Determine whether the given bean name is already in use within this registry,
	 * i.e. whether there is a local bean or alias registered under this name.
	 * @param beanName the name to check
	 * @return whether the given bean name is already in use
	 */
	boolean isBeanNameInUse(String beanName);

}

主要定义了BeanDefinition的注册,删除和获取等方法

ApplicationContext

应用程序上下文根接口继承BeanFactory接口,且继承另外几个接口,这几个接口提供的能力就是ApplicationContext除了BeanFactory本身能力以外的核心能力。

源码:

java 复制代码
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
		MessageSource, ApplicationEventPublisher, ResourcePatternResolver 

从父接口的名字,大概知道其核心能力包括:环境配置,访问Bean,加载资源文件,事件发布监听等核心能力。

环境配置:加在application.yaml等配置文件,命令行参数,环境变量,操作系统环境等

事件监听:Springbean的生命周期事件

这个接口本身代码不多,就几个看起来不太重要的接口:

java 复制代码
	@Nullable
	String getId();

	/**
	 * Return a name for the deployed application that this context belongs to.
	 * @return a name for the deployed application, or the empty String by default
	 */
	String getApplicationName();

	/**
	 * Return a friendly name for this context.
	 * @return a display name for this context (never {@code null})
	 */
	String getDisplayName();

	/**
	 * Return the timestamp when this context was first loaded.
	 * @return the timestamp (ms) when this context was first loaded
	 */
	long getStartupDate();

	/**
	 * Return the parent context, or {@code null} if there is no parent
	 * and this is the root of the context hierarchy.
	 * @return the parent context, or {@code null} if there is no parent
	 */
	@Nullable
	ApplicationContext getParent();
/**
	 * Expose AutowireCapableBeanFactory functionality for this context.
	 * <p>This is not typically used by application code, except for the purpose of
	 * initializing bean instances that live outside the application context,
	 * applying the Spring bean lifecycle (fully or partly) to them.
	 * <p>Alternatively, the internal BeanFactory exposed by the
	 * {@link ConfigurableApplicationContext} interface offers access to the
	 * {@link AutowireCapableBeanFactory} interface too. The present method mainly
	 * serves as a convenient, specific facility on the ApplicationContext interface.
	 * <p><b>NOTE: As of 4.2, this method will consistently throw IllegalStateException
	 * after the application context has been closed.</b> In current Spring Framework
	 * versions, only refreshable application contexts behave that way; as of 4.2,
	 * all application context implementations will be required to comply.
	 * @return the AutowireCapableBeanFactory for this context
	 * @throws IllegalStateException if the context does not support the
	 * {@link AutowireCapableBeanFactory} interface, or does not hold an
	 * autowire-capable bean factory yet (e.g. if {@code refresh()} has
	 * never been called), or if the context has been closed already
	 * @see ConfigurableApplicationContext#refresh()
	 * @see ConfigurableApplicationContext#getBeanFactory()
	 */
	AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;

ConfigurableApplicationContext

可配置应用程序上下文,关键在:可配置的。就是可以修改Application,从其提供的接口方法中,我们可以看可以修改什么:

java 复制代码
/**
	 * Set the unique id of this application context.
	 * @since 3.0
	 */
	void setId(String id);

	/**
	 * Set the parent of this application context.
	 * <p>Note that the parent shouldn't be changed: It should only be set outside
	 * a constructor if it isn't available when an object of this class is created,
	 * for example in case of WebApplicationContext setup.
	 * @param parent the parent context
	 * @see org.springframework.web.context.ConfigurableWebApplicationContext
	 */
	void setParent(@Nullable ApplicationContext parent);

	/**
	 * Set the {@code Environment} for this application context.
	 * @param environment the new environment
	 * @since 3.1
	 */
	void setEnvironment(ConfigurableEnvironment environment);

	/**
	 * Return the {@code Environment} for this application context in configurable
	 * form, allowing for further customization.
	 * @since 3.1
	 */
	@Override
	ConfigurableEnvironment getEnvironment();

	/**
	 * Set the {@link ApplicationStartup} for this application context.
	 * <p>This allows the application context to record metrics
	 * during startup.
	 * @param applicationStartup the new context event factory
	 * @since 5.3
	 */
	void setApplicationStartup(ApplicationStartup applicationStartup);

	/**
	 * Return the {@link ApplicationStartup} for this application context.
	 * @since 5.3
	 */
	ApplicationStartup getApplicationStartup();

	/**
	 * Add a new BeanFactoryPostProcessor that will get applied to the internal
	 * bean factory of this application context on refresh, before any of the
	 * bean definitions get evaluated. To be invoked during context configuration.
	 * @param postProcessor the factory processor to register
	 */
	void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor);

	/**
	 * Add a new ApplicationListener that will be notified on context events
	 * such as context refresh and context shutdown.
	 * <p>Note that any ApplicationListener registered here will be applied
	 * on refresh if the context is not active yet, or on the fly with the
	 * current event multicaster in case of a context that is already active.
	 * @param listener the ApplicationListener to register
	 * @see org.springframework.context.event.ContextRefreshedEvent
	 * @see org.springframework.context.event.ContextClosedEvent
	 */
	void addApplicationListener(ApplicationListener<?> listener);

	/**
	 * Specify the ClassLoader to load class path resources and bean classes with.
	 * <p>This context class loader will be passed to the internal bean factory.
	 * @since 5.2.7
	 * @see org.springframework.core.io.DefaultResourceLoader#DefaultResourceLoader(ClassLoader)
	 * @see org.springframework.beans.factory.config.ConfigurableBeanFactory#setBeanClassLoader
	 */
	void setClassLoader(ClassLoader classLoader);

	/**
	 * Register the given protocol resolver with this application context,
	 * allowing for additional resource protocols to be handled.
	 * <p>Any such resolver will be invoked ahead of this context's standard
	 * resolution rules. It may therefore also override any default rules.
	 * @since 4.3
	 */
	void addProtocolResolver(ProtocolResolver resolver);

	/**
	 * Load or refresh the persistent representation of the configuration, which
	 * might be from Java-based configuration, an XML file, a properties file, a
	 * relational database schema, or some other format.
	 * <p>As this is a startup method, it should destroy already created singletons
	 * if it fails, to avoid dangling resources. In other words, after invocation
	 * of this method, either all or no singletons at all should be instantiated.
	 * @throws BeansException if the bean factory could not be initialized
	 * @throws IllegalStateException if already initialized and multiple refresh
	 * attempts are not supported
	 */
	void refresh() throws BeansException, IllegalStateException;

	/**
	 * Register a shutdown hook with the JVM runtime, closing this context
	 * on JVM shutdown unless it has already been closed at that time.
	 * <p>This method can be called multiple times. Only one shutdown hook
	 * (at max) will be registered for each context instance.
	 * <p>As of Spring Framework 5.2, the {@linkplain Thread#getName() name} of
	 * the shutdown hook thread should be {@link #SHUTDOWN_HOOK_THREAD_NAME}.
	 * @see java.lang.Runtime#addShutdownHook
	 * @see #close()
	 */
	void registerShutdownHook();

由代码可知,主要包括设置父上下文、设置环境、增加BeanFactoryPostProcessor、增加应用程序监听器等。

ApplicationContext除BeanFactory外的其他父接口

ResourcePatternResolver

这个接口的官方注释翻译如下:

java 复制代码
/**

用于将位置模式(例如,Ant样式的路径模式)解析为{@link Resource}对象的策略接口。
这是{@link org.springframework.core.io.ResourceLoader}接口的扩展。传入的{@code ResourceLoader}(例如,在上下文中运行时通过

{@link org.springframework.context.ResourceLoaderAware}传入的{@link org.springframework.context.ApplicationContext})可以检查它是否也实现了这个扩展接口。
{@link PathMatchingResourcePatternResolver}是一个独立的实现,可以在{@code ApplicationContext}之外使用,也被

{@link ResourceArrayPropertyEditor}用于填充{@code Resource}数组bean属性。
可以与任何类型的位置模式一起使用------例如,{@code "/WEB-INF/*-context.xml"}。但是,输入模式必须与策略实现匹配。此接口仅指定转换方法,而不是特定的模式格式。

此接口还为所有来自类路径的匹配资源定义了{@code "classpath*:"}资源前缀。请注意,资源位置也可能包含占位符------例如{@code "/beans-*.xml"}。JAR文件或类路径中的不同目录可以包含相同名称的多个文件。

@author Juergen Hoeller
@since 1.0.2
@see org.springframework.core.io.Resource
@see org.springframework.core.io.ResourceLoader
@see org.springframework.context.ApplicationContext
@see org.springframework.context.ResourceLoaderAware */

主要是用于将路径解析为Resource对象,供后续读取

EnvironmentCapable

该接口提供获取Environment对象的接口,用于获取spring英勇的环境信息。接口仅有一个方法:

java 复制代码
public interface EnvironmentCapable {

	/**
	 * Return the {@link Environment} associated with this component.
	 */
	Environment getEnvironment();

}

MessageSource

国际化支持组件,主要定义了getMessage方法,用于获取不同语言的信息。下面给一个方法的源码看看:

java 复制代码
/**
	 * Try to resolve the message. Return default message if no message was found.
	 * @param code the message code to look up, e.g. 'calculator.noRateSet'.
	 * MessageSource users are encouraged to base message names on qualified class
	 * or package names, avoiding potential conflicts and ensuring maximum clarity.
	 * @param args an array of arguments that will be filled in for params within
	 * the message (params look like "{0}", "{1,date}", "{2,time}" within a message),
	 * or {@code null} if none
	 * @param defaultMessage a default message to return if the lookup fails
	 * @param locale the locale in which to do the lookup
	 * @return the resolved message if the lookup was successful, otherwise
	 * the default message passed as a parameter (which may be {@code null})
	 * @see #getMessage(MessageSourceResolvable, Locale)
	 * @see java.text.MessageFormat
	 */
	@Nullable
	String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale);

ApplicationEventPublisher

事件发布器,在spring的应用生命周期中,会发布各种事件,注册的监听器收到事件之后可以执行相应的逻辑。这是发布-订阅模式的应用。主要有以下4个对象:

事件源:发布事件的对象

事件:事件源发布的信息/做出的动作

广播器:事件真正广播给监听器的对象,执行事件广播动作,这里EventPublisher就是

监听器:监听事件的对象

这个接口注意定义了publishEvent方法,用于发布事件。

AbstractApplicationContext

此抽象类开始实现了父接口定义的大部分特性和功能。采用模板方法模式,具体的实现还是要子类来完成。它仅简单实现应用上下文的基本功能,不强制约束配置的承载形式(XML、注解驱动等)。

与普通BeanFactory相比,ApplicationContext能够检查在其内部bean对象工厂中定义的特殊Bean,这类自动注册在上下文中定义为Bean的包括BeanfactoryPostProcessors、BeanPostProcessors和ApplicationListeners。

ApplicationContext实现国际化接口和事件广播器的方式可以是通过提供内部bean的方式,比如一个messageSource的Bean完成国际化的能力。一个applicationEventMulticaster的bean完成事件广播器功能。

加载资源文件一般是继承DefaultResourceLoader的策略,从类路径下加载;但是可以被子类覆盖,比如在web场景中,可以从ServletContext中加载(扩展的子类ServletContextResourceLoader)。

GenericApplicationContext

继承关系如下:

java 复制代码
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry

此类是注解驱动IOC容器的第一个非抽象实现,已经具备ApplicationContext所有的基本能力了。

内部持有一个DefaultListableBeanFactory,实现了BeanDefinitionRegistry接口,允许任何Bean定义读取其应用于此容器中。ApplicationContext通过组合BeanFactory的方式获得容器功能

实现了BeanDefinitionRegistry接口,可以自定义注册一些Bean,其实现是由DefaultListableBeanFactory的方法完成,采用的是委托机制:

java 复制代码
@Override
	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {

		this.beanFactory.registerBeanDefinition(beanName, beanDefinition);
	}

其内部持有的DefaultListableBeanFactory在构造函数中初始化了,只允许刷新一次。

AnnotationConfigApplicationContext

继承关系如下

java 复制代码
public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry

独立的注解驱动的ApplicationContext,继承GenericApplicationContext,BeanFactory仅能刷新一次。接受组件类作为输入(特别是@Configuration注解的类),一个@Configuration注解的类相当于一个XML配置文件,注解配置类就是驱动加载源。

它允许使用register(Class...)方法直接传入指定的配置类,也可以采用scan进行类路径的包扫描。如果有多个@Configuration注解类,后面的类中定义的Bean配置会覆盖前面的相同定义。

这个类代码不是很多,直接看源码;

java 复制代码
/**
 * Standalone application context, accepting <em>component classes</em> as input &mdash;
 * in particular {@link Configuration @Configuration}-annotated classes, but also plain
 * {@link org.springframework.stereotype.Component @Component} types and JSR-330 compliant
 * classes using {@code javax.inject} annotations.
 *
 * <p>Allows for registering classes one by one using {@link #register(Class...)}
 * as well as for classpath scanning using {@link #scan(String...)}.
 *
 * <p>In case of multiple {@code @Configuration} classes, {@link Bean @Bean} methods
 * defined in later classes will override those defined in earlier classes. This can
 * be leveraged to deliberately override certain bean definitions via an extra
 * {@code @Configuration} class.
 *
 * <p>See {@link Configuration @Configuration}'s javadoc for usage examples.
 *
 * @author Juergen Hoeller
 * @author Chris Beams
 * @since 3.0
 * @see #register
 * @see #scan
 * @see AnnotatedBeanDefinitionReader
 * @see ClassPathBeanDefinitionScanner
 * @see org.springframework.context.support.GenericXmlApplicationContext
 */
public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {

	private final AnnotatedBeanDefinitionReader reader;

	private final ClassPathBeanDefinitionScanner scanner;


	/**
	 * Create a new AnnotationConfigApplicationContext that needs to be populated
	 * through {@link #register} calls and then manually {@linkplain #refresh refreshed}.
	 */
	public AnnotationConfigApplicationContext() {
		StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");
		this.reader = new AnnotatedBeanDefinitionReader(this);
		createAnnotatedBeanDefReader.end();
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}

	/**
	 * Create a new AnnotationConfigApplicationContext with the given DefaultListableBeanFactory.
	 * @param beanFactory the DefaultListableBeanFactory instance to use for this context
	 */
	public AnnotationConfigApplicationContext(DefaultListableBeanFactory beanFactory) {
		super(beanFactory);
		this.reader = new AnnotatedBeanDefinitionReader(this);
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}

	/**
	 * Create a new AnnotationConfigApplicationContext, deriving bean definitions
	 * from the given component classes and automatically refreshing the context.
	 * @param componentClasses one or more component classes &mdash; for example,
	 * {@link Configuration @Configuration} classes
	 */
	public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
		this();
		register(componentClasses);
		refresh();
	}

	/**
	 * Create a new AnnotationConfigApplicationContext, scanning for components
	 * in the given packages, registering bean definitions for those components,
	 * and automatically refreshing the context.
	 * @param basePackages the packages to scan for component classes
	 */
	public AnnotationConfigApplicationContext(String... basePackages) {
		this();
		scan(basePackages);
		refresh();
	}


	/**
	 * Propagate the given custom {@code Environment} to the underlying
	 * {@link AnnotatedBeanDefinitionReader} and {@link ClassPathBeanDefinitionScanner}.
	 */
	@Override
	public void setEnvironment(ConfigurableEnvironment environment) {
		super.setEnvironment(environment);
		this.reader.setEnvironment(environment);
		this.scanner.setEnvironment(environment);
	}

	/**
	 * Provide a custom {@link BeanNameGenerator} for use with {@link AnnotatedBeanDefinitionReader}
	 * and/or {@link ClassPathBeanDefinitionScanner}, if any.
	 * <p>Default is {@link AnnotationBeanNameGenerator}.
	 * <p>Any call to this method must occur prior to calls to {@link #register(Class...)}
	 * and/or {@link #scan(String...)}.
	 * @see AnnotatedBeanDefinitionReader#setBeanNameGenerator
	 * @see ClassPathBeanDefinitionScanner#setBeanNameGenerator
	 * @see AnnotationBeanNameGenerator
	 * @see FullyQualifiedAnnotationBeanNameGenerator
	 */
	public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) {
		this.reader.setBeanNameGenerator(beanNameGenerator);
		this.scanner.setBeanNameGenerator(beanNameGenerator);
		getBeanFactory().registerSingleton(
				AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, beanNameGenerator);
	}

	/**
	 * Set the {@link ScopeMetadataResolver} to use for registered component classes.
	 * <p>The default is an {@link AnnotationScopeMetadataResolver}.
	 * <p>Any call to this method must occur prior to calls to {@link #register(Class...)}
	 * and/or {@link #scan(String...)}.
	 */
	public void setScopeMetadataResolver(ScopeMetadataResolver scopeMetadataResolver) {
		this.reader.setScopeMetadataResolver(scopeMetadataResolver);
		this.scanner.setScopeMetadataResolver(scopeMetadataResolver);
	}


	//---------------------------------------------------------------------
	// Implementation of AnnotationConfigRegistry
	//---------------------------------------------------------------------

	/**
	 * Register one or more component classes to be processed.
	 * <p>Note that {@link #refresh()} must be called in order for the context
	 * to fully process the new classes.
	 * @param componentClasses one or more component classes &mdash; for example,
	 * {@link Configuration @Configuration} classes
	 * @see #scan(String...)
	 * @see #refresh()
	 */
	@Override
	public void register(Class<?>... componentClasses) {
		Assert.notEmpty(componentClasses, "At least one component class must be specified");
		StartupStep registerComponentClass = this.getApplicationStartup().start("spring.context.component-classes.register")
				.tag("classes", () -> Arrays.toString(componentClasses));
		this.reader.register(componentClasses);
		registerComponentClass.end();
	}

	/**
	 * Perform a scan within the specified base packages.
	 * <p>Note that {@link #refresh()} must be called in order for the context
	 * to fully process the new classes.
	 * @param basePackages the packages to scan for component classes
	 * @see #register(Class...)
	 * @see #refresh()
	 */
	@Override
	public void scan(String... basePackages) {
		Assert.notEmpty(basePackages, "At least one base package must be specified");
		StartupStep scanPackages = this.getApplicationStartup().start("spring.context.base-packages.scan")
				.tag("packages", () -> Arrays.toString(basePackages));
		this.scanner.scan(basePackages);
		scanPackages.end();
	}


	//---------------------------------------------------------------------
	// Adapt superclass registerBean calls to AnnotatedBeanDefinitionReader
	//---------------------------------------------------------------------

	@Override
	public <T> void registerBean(@Nullable String beanName, Class<T> beanClass,
			@Nullable Supplier<T> supplier, BeanDefinitionCustomizer... customizers) {

		this.reader.registerBean(beanClass, beanName, supplier, customizers);
	}

}

可见,其主要方法是register和scan,分别委托给其他类AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner实现。

相关推荐
minDuck2 分钟前
ruoyi-vue集成tianai-captcha验证码
java·前端·vue.js
为将者,自当识天晓地。21 分钟前
c++多线程
java·开发语言
daqinzl29 分钟前
java获取机器ip、mac
java·mac·ip
激流丶44 分钟前
【Kafka 实战】如何解决Kafka Topic数量过多带来的性能问题?
java·大数据·kafka·topic
Themberfue1 小时前
Java多线程详解⑤(全程干货!!!)线程安全问题 || 锁 || synchronized
java·开发语言·线程·多线程·synchronized·
千天夜1 小时前
使用UDP协议传输视频流!(分片、缓存)
python·网络协议·udp·视频流
让学习成为一种生活方式1 小时前
R包下载太慢安装中止的解决策略-R语言003
java·数据库·r语言
晨曦_子画1 小时前
编程语言之战:AI 之后的 Kotlin 与 Java
android·java·开发语言·人工智能·kotlin
南宫生2 小时前
贪心算法习题其三【力扣】【算法学习day.20】
java·数据结构·学习·算法·leetcode·贪心算法
Heavydrink2 小时前
HTTP动词与状态码
java