Bootstrap

Spring源码八:容器扩展一

上一篇我们结束了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 方法解析,以及它通常执行的任务:

  1. 设置类加载器prepareBeanFactory 方法会为 BeanFactory 设置一个合适的类加载器,这样bean在加载时可以使用正确的类加载器来加载所需的类。

  2. 设置序列化ID:为了防止序列化过程中的问题,Spring会为 BeanFactory 设置一个唯一的序列化ID。

  3. 添加几个默认的属性编辑器:Spring框架会添加一些默认的属性编辑器,这些编辑器可以自动转换属性值,比如将字符串转换为 Properties 对象或其他类型。

  4. 注册一些特殊的bean名称:例如,BeanFactory 本身可以作为一个bean注册到容器中,通常它的ID是 "beanFactory"

  5. 允许自定义扩展BeanFactory 提供了一些方法,如 addBeanPostProcessorregisterSingleton,允许开发者在容器初始化过程中添加自定义的后处理器或注册单例bean。

  6. 设置一些默认的BeanFactory特性:比如是否允许bean定义覆盖等。

  7. 初始化属性编辑器注册器:正如您之前提到的,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 做了以下几件事情:

  1. 创建SpEL编译器spelCompiler 是用于编译SpEL表达式的组件。

  2. 创建评估上下文evaluationContext 是SpEL表达式评估时使用的上下文,它包含了解析表达式所需的各种信息和设置。

  3. 添加属性解析器BeanFactoryPropertyBeanPropertyAccessor 是一个属性解析器,它允许SpEL表达式访问bean的属性。

  4. 设置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的初始化过程中插入自定义逻辑。这个接口提供了两个方法:

  1. postProcessBeforeInitialization(Object bean, String beanName):在bean的初始化方法(例如 afterPropertiesSet 或自定义的初始化方法)调用之前执行。
  2. 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

;