上一篇我们结束了refresh方法中的obtainBeanFactory方法,得到了一个初级容器BeanFactory实例对象,并对该对象进行了最基本的设置(是否支持覆盖与循环引用)。并设置了最基础的成员属性beanDefinitionMap(存放BeanDefiniton的容器),这一章开始我们来逐步看下Spring是如何扩展这个基础的容器,是他拥有更高级的功能。
咱们继续回到Spring的refresh方法如下:
{
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing. 1、初始化上下文信息,替换占位符、必要参数的校验
prepareRefresh();
// Tell the subclass to refresh the internal bean factory. 2、解析类Xml、初始化BeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 这一步主要是对初级容器的基础设计
// Prepare the bean factory for use in this context. 3、准备BeanFactory内容:
//!!!!!!!!!!!! 这里 这里 今天看这里 !!!!!!!!!!!//
prepareBeanFactory(beanFactory); // 对beanFactory容器的功能的扩展:
try {
// Allows post-processing of the bean factory in context subclasses. 4、扩展点加一:空实现,主要用于处理特殊Bean的后置处理器
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context. 5、spring bean容器的后置处理器
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation. 6、注册bean的后置处理器
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context. 7、初始化消息源
initMessageSource();
// Initialize event multicaster for this context. 8、初始化事件广播器
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses. 9、扩展点加一:空实现;主要是在实例化之前做些bean初始化扩展
onRefresh();
// Check for listener beans and register them. 10、初始化监听器
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons. 11、实例化:非兰加载Bean
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event. 12、发布相应的事件通知
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
prepareBeanFactory
/**
* Configure the factory's standard context characteristics,
* such as the context's ClassLoader and post-processors.
* @param beanFactory the BeanFactory to configure
*/
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// Tell the internal bean factory to use the context's class loader etc.
// 设置beanFactory类加载器,默认给当前上下文加载器
beanFactory.setBeanClassLoader(getClassLoader());
// 设置标准表达式解析器:#{}
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
// 包装资源编辑器
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// Configure the bean factory with context callbacks.
// 添加beanPostProcessor,ApplicationContextAwareProcessor此类用来完成某些Aware对象的注入
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
// 设置要忽略自动装配的接口,这些接口的实现是由容器通过set方法进行注入的,使用autowire进行注入的时候需要将这些接口进行忽略
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
// BeanFactory interface not registered as resolvable type in a plain factory.
// MessageSource registered (and found for autowiring) as a bean.
// 设置几个自动装配的特殊规则,当在进行ioc初始化的如果有多个实现,那么就使用指定的对象进行注入
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
// Register early post-processor for detecting inner beans as ApplicationListeners.
// // 注册BeanPostProcessor
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
// Detect a LoadTimeWeaver and prepare for weaving, if found.
// 增加对AspectJ的支持,在java中织入分为三种方式,分为编译器织入,类加载器织入,运行期织入,编译器织入是指在java编译器,采用特殊的编译器,将切面织入到java类中,
// 而类加载期织入则指通过特殊的类加载器,在类字节码加载到JVM时,织入切面,运行期织入则是采用cglib和jdk进行切面的织入
// aspectj提供了两种织入方式,第一种是通过特殊编译器,在编译器,将aspectj语言编写的切面类织入到java类中,第二种是类加载期织入,就是下面的load time weaving,此处后续讲
if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
// Set a temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
// Register default environment beans. // 注册默认的系统环境bean到一级缓存中
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
}
if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
}
if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
}
}
方法总览
在Spring框架中,prepareBeanFactory
方法是 AbstractApplicationContext
类中的一个受保护方法,是Spring容器初始化过程中的一个重要步骤,它确保了容器在加载bean之前具备了正确的配置和功能扩展。
以下是对 prepareBeanFactory
方法解析,以及它通常执行的任务:
-
设置类加载器:
prepareBeanFactory
方法会为BeanFactory
设置一个合适的类加载器,这样bean在加载时可以使用正确的类加载器来加载所需的类。 -
设置序列化ID:为了防止序列化过程中的问题,Spring会为
BeanFactory
设置一个唯一的序列化ID。 -
添加几个默认的属性编辑器:Spring框架会添加一些默认的属性编辑器,这些编辑器可以自动转换属性值,比如将字符串转换为
Properties
对象或其他类型。 -
注册一些特殊的bean名称:例如,
BeanFactory
本身可以作为一个bean注册到容器中,通常它的ID是"beanFactory"
。 -
允许自定义扩展:
BeanFactory
提供了一些方法,如addBeanPostProcessor
和registerSingleton
,允许开发者在容器初始化过程中添加自定义的后处理器或注册单例bean。 -
设置一些默认的BeanFactory特性:比如是否允许bean定义覆盖等。
-
初始化属性编辑器注册器:正如您之前提到的,
ResourceEditorRegistrar
就是在这里被添加到BeanFactory
中,以便自动转换资源路径。
prepareBeanFactory
方法是Spring容器初始化流程中的一个关键环节,它确保了容器在加载bean之前已经具备了正确的配置和功能扩展。通过这个方法,Spring框架能够提供灵活的配置选项和强大的扩展能力。
beanExpressionResolver
在Spring框架中,表达式语言通常指的是Spring Expression Language(SpEL),它是Spring提供的一种强大的表达式语言,用于在运行时查询和操作对象图。SpEL 支持在属性文件、注解以及XML配置中使用表达式。
StandardBeanExpressionResolver
是Spring框架中用于解析SpEL表达式的组件。它作为 BeanFactory
的功能扩展点之一,允许在bean的属性值中使用SpEL表达式。
让我们简单看一下 StandardBeanExpressionResolver
的构造方法,了解它是如何初始化的:
public StandardBeanExpressionResolver() {
this.spelCompiler = new SpelCompiler(this);
this.evaluationContext = new StandardEvaluationContext();
// 这里可以添加自定义的属性解析器
this.evaluationContext.addPropertyAccessor(new BeanFactoryPropertyBeanPropertyAccessor());
// 允许在SpEL表达式中使用beanFactory
this.evaluationContext.setBeanResolver(new BeanNameBeanResolver());
}
在构造方法中,StandardBeanExpressionResolver
做了以下几件事情:
-
创建SpEL编译器:
spelCompiler
是用于编译SpEL表达式的组件。 -
创建评估上下文:
evaluationContext
是SpEL表达式评估时使用的上下文,它包含了解析表达式所需的各种信息和设置。 -
添加属性解析器:
BeanFactoryPropertyBeanPropertyAccessor
是一个属性解析器,它允许SpEL表达式访问bean的属性。 -
设置bean解析器:
BeanNameBeanResolver
是一个bean解析器,它允许SpEL表达式通过bean名称来解析和访问bean。
通过将 StandardBeanExpressionResolver
添加到 BeanFactory
中,Spring框架允许开发者在bean的定义和配置中使用SpEL表达式,从而提供了一种灵活的方式来注入依赖和操作对象图。这使得配置更加动态和强大。之前我们通过Spring预留的保护接口可以动态实现自定义功能,这里也可以通过el表达式来实现动态配置。
标准的默认spel表达式,可以看到其实就是#{} 占位符,这里留个心眼后续我们在属性注入的时候替换占位符里面的内容时,再回头看看这里。
propertyEditorRegistrars
是一个Linked HashSet结构,在进入这个类看下:
/**
* Populate the given {@code registry} with the following resource editors:
* ResourceEditor, InputStreamEditor, InputSourceEditor, FileEditor, URLEditor,
* URIEditor, ClassEditor, ClassArrayEditor.
* <p>If this registrar has been configured with a {@link ResourcePatternResolver},
* a ResourceArrayPropertyEditor will be registered as well.
* @see org.springframework.core.io.ResourceEditor
* @see org.springframework.beans.propertyeditors.InputStreamEditor
* @see org.springframework.beans.propertyeditors.InputSourceEditor
* @see org.springframework.beans.propertyeditors.FileEditor
* @see org.springframework.beans.propertyeditors.URLEditor
* @see org.springframework.beans.propertyeditors.URIEditor
* @see org.springframework.beans.propertyeditors.ClassEditor
* @see org.springframework.beans.propertyeditors.ClassArrayEditor
* @see org.springframework.core.io.support.ResourceArrayPropertyEditor
*/
@Override
public void registerCustomEditors(PropertyEditorRegistry registry) {
ResourceEditor baseEditor = new ResourceEditor(this.resourceLoader, this.propertyResolver);
doRegisterEditor(registry, Resource.class, baseEditor);
doRegisterEditor(registry, ContextResource.class, baseEditor);
doRegisterEditor(registry, InputStream.class, new InputStreamEditor(baseEditor));
doRegisterEditor(registry, InputSource.class, new InputSourceEditor(baseEditor));
doRegisterEditor(registry, File.class, new FileEditor(baseEditor));
doRegisterEditor(registry, Path.class, new PathEditor(baseEditor));
doRegisterEditor(registry, Reader.class, new ReaderEditor(baseEditor));
doRegisterEditor(registry, URL.class, new URLEditor(baseEditor));
ClassLoader classLoader = this.resourceLoader.getClassLoader();
doRegisterEditor(registry, URI.class, new URIEditor(classLoader));
doRegisterEditor(registry, Class.class, new ClassEditor(classLoader));
doRegisterEditor(registry, Class[].class, new ClassArrayEditor(classLoader));
if (this.resourceLoader instanceof ResourcePatternResolver) {
doRegisterEditor(registry, Resource[].class,
new ResourceArrayPropertyEditor((ResourcePatternResolver) this.resourceLoader, this.propertyResolver));
}
}
/**
* Override default editor, if possible (since that's what we really mean to do here);
* otherwise register as a custom editor.
*/
private void doRegisterEditor(PropertyEditorRegistry registry, Class<?> requiredType, PropertyEditor editor) {
if (registry instanceof PropertyEditorRegistrySupport) {
((PropertyEditorRegistrySupport) registry).overrideDefaultEditor(requiredType, editor);
}
else {
registry.registerCustomEditor(requiredType, editor);
}
}
ResourceEditorRegistrar
是Spring提供的一个属性编辑器注册器,它的作用是将字符串形式的资源路径转换为 Resource
对象。这在处理文件上传、数据库连接等需要访问外部资源的场景中非常有用。
通过这行代码,Spring容器在创建bean时,如果bean的某个属性是字符串形式的资源路径,ResourceEditorRegistrar
会自动将这个字符串转换为 Resource
对象,然后注入到bean的相应属性中。这样,开发者就可以在bean的定义中使用资源路径字符串,而不需要手动创建和配置 Resource
对象。同理还可以创建其他类型对象。
所以这里的主要作用就是将String类型转换为Spring支持的不同类型对象。早期Spring都是依靠xml进行属性的配置,而xml配置的属性只能是String其他复杂的资源类对象就需要通过String进行转换。
beanPostProcessors
可以看到addBeanPostProcessor是将ApplicationContextAwareProcessor实例添加到BeanFactory属性中,我们点进去可以看到prostProcessBeforeInitialization方法。
看到这个方法,首先来了一个判断一看就是Aware接口的子类且如果是判断里面六个类就不做处理了,所以我们接着往下看:invokeAwareInterfaces方法为核心往下看
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
可以看到如果我们注入的Bean是上述六个接口的子类,这一步会将Spring容器中的对应属性设置到我们自定义的Bean中,这些Aware接口就是我们对Spring容器的感知接口。需要Spring那些属性就实现上述接口就完事。
假设我们有一个自定义的 MyBean
类,它需要访问Spring的 ApplicationContext
来获取其他bean的引用或者访问特定的配置属性。为了实现这一点,我们可以让我们的 MyBean
类实现 ApplicationContextAware
接口:
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class MyBean implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public void doSomething() {
// 使用ApplicationContext获取其他bean或者配置属性
SomeOtherBean someOtherBean = applicationContext.getBean(SomeOtherBean.class);
String configValue = applicationContext.getEnvironment().getProperty("some.config.value");
// 执行一些操作...
}
}
ApplicationContextAwareProcessor
是继承自 BeanPostProcessor
接口的一个具体实现。BeanPostProcessor
接口是Spring框架中非常强大的扩展点之一,允许开发者在bean的初始化过程中插入自定义逻辑。这个接口提供了两个方法:
postProcessBeforeInitialization(Object bean, String beanName)
:在bean的初始化方法(例如afterPropertiesSet
或自定义的初始化方法)调用之前执行。postProcessAfterInitialization(Object bean, String beanName)
:在bean初始化方法调用之后执行。
ApplicationContextAwareProcessor
利用了 BeanPostProcessor
接口,主要是为了实现对实现了 ApplicationContextAware
接口的bean进行处理。具体来说,它的逻辑通常位于 postProcessBeforeInitialization
方法中。以下是 ApplicationContextAwareProcessor
中这个方法可能的实现逻辑:
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof ApplicationContextAware) {
// 将ApplicationContext的引用注入到实现了ApplicationContextAware接口的bean中
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
return bean;
}
通过这种方式,ApplicationContextAwareProcessor
确保所有实现了 ApplicationContextAware
接口的bean都能在它们自己的初始化逻辑之前获得 ApplicationContext
的引用,从而可以在需要时访问Spring容器中的其他bean或配置信息。
BeanPostProcessor
的这种机制为Spring容器提供了极大的灵活性,允许开发者在bean的生命周期的特定点插入自定义逻辑,无论是用于依赖注入、事件处理还是其他自定义的初始化逻辑。
ignoreDependencyInterface
意味着这些依赖的注入过程完全由Spring容器管理,确保了依赖的正确性和一致性。刚好上述addBeanPostProcessor方法add进入的Awre接口也是这六个。
registerResolvableDependency
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
这行代码在Spring框架中用于注册一个可以解析的依赖,这样当某个Bean需要依赖一个 BeanFactory
类型的实例时,Spring容器会自动提供当前的 beanFactory
实例。
作用
这种方式确保了当某个Bean实现了特定的接口或者需要依赖某个类型的实例时,Spring容器能够自动注入正确的依赖。
其他配置项
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
// Detect a LoadTimeWeaver and prepare for weaving, if found.
// 增加对AspectJ的支持,在java中织入分为三种方式,分为编译器织入,类加载器织入,运行期织入,编译器织入是指在java编译器,采用特殊的编译器,将切面织入到java类中,
// 而类加载期织入则指通过特殊的类加载器,在类字节码加载到JVM时,织入切面,运行期织入则是采用cglib和jdk进行切面的织入
// aspectj提供了两种织入方式,第一种是通过特殊编译器,在编译器,将aspectj语言编写的切面类织入到java类中,第二种是类加载期织入,就是下面的load time weaving,此处后续讲
if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
// Set a temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
// Register default environment beans. // 注册默认的系统环境bean到一级缓存中
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
}
if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
}
if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
}
以上代码展示了如何配置Spring的bean工厂,包括添加 BeanPostProcessor
来检测和处理特定类型的bean,支持AspectJ的加载期织入,设置临时的 ClassLoader
进行类型匹配,以及注册默认的系统环境bean。这些配置确保了Spring应用程序的灵活性和可扩展性。
sql