(五)Spring源码解析:ApplicationContext解析

一、概述

1.1> 整体概览

在前面的内容中,我们针对BeanFactory进行了深度的分析。那么,下面我们将针对BeanFactory的功能扩展类ApplicationContext进行深度的分析。ApplicationConext与BeanFactory的功能相似,都是用于向IOC中加载Bean的。由于ApplicationConext的功能是大于BeanFactory的,所以在日常使用中,建议直接使用ApplicationConext即可。下面是两个类使用示例:

ApplicationConext是接口,所以要分析其加载bean的流程,就可以从ClassPathXmlApplicationContext入手,其构造函数如下所示:

public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
    this(new String[] {configLocation}, true, null);
}

public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) {
    super(parent);
    setConfigLocations(configLocations); // 事情1:设置配置加载路径
    if (refresh) refresh(); // 事情2:所有功能的初始化操作
}

在上面的构造方法中,主要做了两个事情:
事情1】设置配置的加载路径
事情2】执行ApplicationConext中,所有功能的初始化操作;
下面文章的内容,我们就是会针对这两部分做详细的解析:

1.2> setConfigLocations(...) 设置配置加载路径

该方法逻辑不多,主要就是为应用上下文ApplicationContext设置配置路径(config locations),源码如下所示:

public void setConfigLocations(String... locations) { // 支持传入多个配置文件
    if (locations != null) {
        this.configLocations = new String[locations.length];
        for (int i = 0; i < locations.length; i++) 
            this.configLocations[i] = resolvePath(locations[i]).trim(); // 路径解析
    } else 
        this.configLocations = null;
}

// 如果路径中包含特殊符号(如:${var}),那么在resolvePath方法中会搜寻匹配的系统变量并且进行替换操作
protected String resolvePath(String path) {
    // 调用AbstractPropertyResolver#resolveRequiredPlaceholders(path)方法
    return getEnvironment().resolveRequiredPlaceholders(path); 
}

1.3> refresh() 初始化

refresh()方法中几乎包含了ApplicationContext中提供的全部功能,下面我们会针对这个方法进行详细的分析:

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        prepareRefresh(); /** 步骤1:为refresh操作做提前的准备工作 */
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); /** 步骤2:获得beanFactory实例对象 */
        prepareBeanFactory(beanFactory); /** 步骤3:准备用于此上下文的beanFactory */
        try {
            postProcessBeanFactory(beanFactory); // 允许在上下文子类中对BeanFactory进行后置处理(空方法,可由子类实现)
            invokeBeanFactoryPostProcessors(beanFactory); /** 步骤4:激活各种BeanFactory的后置处理器 */
            registerBeanPostProcessors(beanFactory); /** 步骤5:注册各种Bean的后置处理器,在getBean时才会被调用 */
            initMessageSource(); /** 步骤6:为上下文初始化消息源(即:国际化处理)*/
            initApplicationEventMulticaster(); /** 步骤7:为上下文初始化应用事件广播器 */
            onRefresh(); // 初始化特定上下文子类中的其他特殊bean(空方法,可由子类实现)
            registerListeners(); /** 步骤8:在所有注册的bean中查找listener bean,并注册到消息广播器中 */
            finishBeanFactoryInitialization(beanFactory); /** 步骤9:初始化剩下的单例(非惰性non-lazy-init)*/
            finishRefresh(); /** 步骤10:完成refresh,通知lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知 */
        }
        catch (BeansException ex) {
            destroyBeans(); // Destroy already created singletons to avoid dangling resources
            cancelRefresh(ex); // Reset 'active' flag
            throw ex;
        }
        finally {resetCommonCaches();}
    }
}

二、prepareRefresh()准备工作

下面我们来分析一下refresh()方法中的prepareRefresh()代码段的处理逻辑:

针对prepareRefresh()方法来说,主要就包含如下注释的两个方法,看似没什么意义,但是如果我们自定义了ClassPathXmlApplicationContext类,那么可以通过重写initPropertySources()方法,来增加逻辑代码:

protected void prepareRefresh() {
    ...
    this.active.set(true); 
    initPropertySources(); // 空方法,可以由子类重写
    getEnvironment().validateRequiredProperties(); /** 验证需要的属性文件是否都已经放入环境中 */
    ...
}
public void validateRequiredProperties() throws MissingRequiredPropertiesException {
    this.propertyResolver.validateRequiredProperties();
}
public void validateRequiredProperties() {
    MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();
    for (String key : this.requiredProperties) 
        // 如果环境中没有配置所需属性,则将缺失的属性放到ex中,并抛出异常
        if (this.getProperty(key) == null) 
            ex.addMissingRequiredProperty(key); 
    if (!ex.getMissingRequiredProperties().isEmpty()) {throw ex;}
}

三、obtainFreshBeanFactory()获得最新BeanFactory

下面我们来分析一下refresh()方法中的obtainFreshBeanFactory()代码段的处理逻辑:

通过obtainFreshBeanFactory()这个方法,ApplicationContext就已经拥有了BeanFactory的全部功能。而这个方法中也包含了前面我们介绍BeanFactory时候对于xml配置的加载过程。

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    refreshBeanFactory(); /** 初始化BeanFactory */
    return getBeanFactory(); 
}

refreshBeanFactory()方法中,在基本容器的基础上,增加了是否允许覆盖 和 是否允许扩展的设置,并提供了注解 @Qualifier 和 @Autowired 的支持。

protected final void refreshBeanFactory() throws BeansException {
    if (hasBeanFactory()) {destroyBeans();closeBeanFactory();}

    try {
        DefaultListableBeanFactory beanFactory = createBeanFactory(); // new一个beanFactory的实例对象
        beanFactory.setSerializationId(getId());
        customizeBeanFactory(beanFactory); // 定制化操作BeanFactory配置
        loadBeanDefinitions(beanFactory); /** 初始化XmlBeanDefinitionReader,并进行xml配置文件读取及解析 */
        this.beanFactory = beanFactory;
    }
    catch (IOException ex) {throw new ApplicationContextException(...);}
}

/** 该方法可以采用子类覆盖的方式改变里面的逻辑内容 */
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
    if (this.allowBeanDefinitionOverriding != null) // 设置是否允许覆盖同名称且不同定义的beanDefinition实例
        beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    if (this.allowCircularReferences != null) // 设置是否允许bean直接存在循环依赖
        beanFactory.setAllowCircularReferences(this.allowCircularReferences);
}

loadBeanDefinitions(beanFactory)方法中,执行了初始化XmlBeanDefinitionReader操作,并且进行xml配置文件读取及解析

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
    // 创建XmlBeanDefinitionReader,用于后续对配置的读取操作
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
    beanDefinitionReader.setEnvironment(this.getEnvironment());
    beanDefinitionReader.setResourceLoader(this);
    beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

    initBeanDefinitionReader(beanDefinitionReader); // 对beanDefinitionReader进行设置,可以被子类覆盖
    loadBeanDefinitions(beanDefinitionReader); /** 加载xml配置信息 */
}

/** 对XmlBeanDefinitionReader进行设置(此方法可以由子类进行覆写) */
protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader) {
    reader.setValidating(this.validating); // validating = true
}

当获得了XmlBeanDefinitionReader之后,我们就可以通过loadBeanDefinitions(beanDefinitionReader)方法对xml配置文件进行读取操作了。其中的reader.loadBeanDefinitions(...)方法我们在前面的BeanFactory配置文件读取操作中已经解析过了,此处就不再赘述。

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
    // 尝试获得xml配置文件的Resource实例集合,并进行配置加载
    Resource[] configResources = getConfigResources();  
    if (configResources != null) reader.loadBeanDefinitions(configResources);

    // 尝试获得xml配置文件路径集合,并进行配置加载
    String[] configLocations = getConfigLocations(); 
    if (configLocations != null) reader.loadBeanDefinitions(configLocations);
}

四、prepareBeanFactory(...)准备用于此上下文的beanFactory

4.1> 整体概览

下面我们来分析一下refresh()方法中的prepareBeanFactory()代码段的处理逻辑:

当执行本方法之前,Spring已经完成了对配置的解析操作,而从本方法开始,ApplicationContext在功能上的扩展也由此展开了。

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    // 将当前上下文的classLoader作为beanFactory的classLoader
    beanFactory.setBeanClassLoader(getClassLoader()); 

    // 是否支持SpEL表达式解析,即:可以使用#{bean.xxx}的形式来调用相关属性值
    if (!shouldIgnoreSpel) 
        beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));

    // 添加1个属性编辑器,它是对bean的属性等设置管理的工具
    beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

    // 添加1个bean的后置处理器
    beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this)); 

    // 设置7个需要忽略自动装配的接口
    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.ignoreDependencyInterface(ApplicationStartupAware.class);

    // 注册4个依赖
    beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
    beanFactory.registerResolvableDependency(ResourceLoader.class, this);
    beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
    beanFactory.registerResolvableDependency(ApplicationContext.class, this);

    // 添加1个bean的后置处理器
    beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

    // 增加对AspectJ的支持
    if (!NativeDetector.inNativeImage() && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
        beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
        beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }

    // 注册4个默认的系统环境bean
    if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) // environment
        beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
    if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) // systemProperties
        beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
    if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) // systemEnvironment
        beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
    if (!beanFactory.containsLocalBean(APPLICATION_STARTUP_BEAN_NAME)) // applicationStartup
        beanFactory.registerSingleton(APPLICATION_STARTUP_BEAN_NAME, getApplicationStartup());
}

4.2> 添加对SpEL表达式的支持

SpEL全称是“Spring Expression Language”,它能在运行时构件复杂表达式存取对象图属性对象方法调用等;它是单独模块,只依赖了core模块,所以可以单独使用。SpEL使用 #{...} 作为界定符,所有在大括号中的字符都会被认定为SpEL,使用方式如下所示:

<bean id="user" value="com.muse.entity.User"/>
<bean ...>
    <property name="name" value="#{user}">
</bean>
    
相当于:
    
<bean id="user" value="com.muse.entity.User"/>
<bean ...>
    <property name="name" ref="user">
</bean>

在 prepareBeanFactory(beanFactory) 方法中,通过beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(...))注册SpEL语言解析器,就可以对SpEL进行解析了。

那么,在注册了解析器后,Spring又是在什么时候调用这个解析器进行解析操作的呢?调用方式如下图所示:

解释】其实就是在Spring进行bean初始化的时候,有一个步骤是——属性填充(即:populateBean()),而在这一步中Spring会调用AbstractAutowireCapableBeanFactory#applyPropertyValues(...)方法来完成功能。而就在这个方法中,会创建BeanDefinitionValueResolver实例对象valueResolver来进行属性值的解析操作。同时,也是在这个步骤中,通过AbstractBeanFactory#evaluateBeanDefinitionString(...)方法去完成SpEL的解析操作

相关源码如下所示:

protected Object evaluateBeanDefinitionString(String value, BeanDefinition beanDefinition) {
    if (this.beanExpressionResolver == null) return value;
    Scope scope = null;
    if (beanDefinition != null) {
        String scopeName = beanDefinition.getScope();
        if (scopeName != null) scope = getRegisteredScope(scopeName);
    }
    // 通过获得的beanExpressionResolver实例调用evaluate(...)方法执行解析操作
    return this.beanExpressionResolver.evaluate(value, new BeanExpressionContext(this, scope));
}

4.3> 添加属性编辑器

下面我们继续来介绍一下beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));这行代码,那么通过调用的方法名称,我们可以猜到它的作用是给BeanFactory实例添加属性编辑器。那么,什么是属性编辑器呢? 在Spring加载bean的时候,可以把基本类型属性注入进来,但是对于类似Date这种复杂属性就无法被识别了。

4.3.1> 属性编辑器的应用

我们假设有一个Schedule类,其中包含了一个Date类型的属性,如果采用默认加载方式的话,是无法将xml中配置的"2023-01-01"值转换为Date类型的。那么,下面我们就来演示一下如何通过自定义属性编辑器来让Spring在加载bean的时候,将String类型的值转换为Date类型的值。如下所示:

@Data
public class Schedule {
    private String name;
    private Date date; // 添加Date类型的属性
}
<bean id="schedule" class="com.muse.springbootdemo.entity.propertyeditor.Schedule">
  <property name="name" value="work"/>
  <property name="date" value="2023-01-01"/> <!-- date是Date类型,需要将String转换为Date -->
</bean>

当我们运行的时候发现报错了,即:无法将配置文件中的String类型转换为Date类型

那么,我们添加用来将String类型转换为Date类型的属性编译器DatePropertyEditor,并将其注入到CustomEditorConfigurer中即可。

public class DatePropertyEditor implements PropertyEditorRegistrar {
    @Override
    public void registerCustomEditors(PropertyEditorRegistry registry) {
        registry.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true));
    }
}
<!-- 注册属性编辑器 -->
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
    <property name="propertyEditorRegistrars"> <!-- 将自定义属性编辑器保存到propertyEditorRegistrars属性中 -->
        <list>
            <bean class="com.muse.springbootdemo.entity.propertyeditor.DatePropertyEditor"/>
        </list>
    </property>
</bean>

在调用registerCustomEditor(...)方法的时候,我们创建了CustomDateEditor实例对象,在此处我们只是来看一下它的类继承关系,下文还会涉及对它的介绍。

4.3.2> 源码解析

通过上面对于注册属性编辑器的配置,我们可以看到自定义的属性编辑器DatePropertyEditor被保存到了CustomEditorConfigurerpropertyEditorRegistrars属性中,那么由于CustomEditorConfigurer类实现了BeanFactoryPostProcessor接口,所以当bean初始化的时候,会调用它的postProcessBeanFactory(...)方法,那么在这个方法中,会将propertyEditorRegistrars属性中的所有属性编辑器添加到beanFactory中。

那么,我们在回头来看 beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment())) 的这行代码,其实也是同样的调用方式:

那么,我们分析到这一步之后,发现自定义属性编辑器都会保存到ConfigurableListableBeanFactory实例对象beanFactory的变量propertyEditorRegistrars中。那么问题来了——保存我们知道了,那什么时候属性编辑器会被调用呢? 其实就是在初始化bean的时候,在initBeanWrapper(...)方法中,会被调用。

那么综上所述,我们来通过一张图,了解一下属性编辑器的注册和使用操作相关的类关系图:

在上面的例子中,我们自定义了一个属性编辑器DatePropertyEditor,其实Spring也内置了自己的属性编辑器ResourceEditorRegistrar

ResourceEditorRegistrarregisterCustomEditors(...)方法中,提供了Spring默认配置的所有属性编辑器集合。如下所示:

doRegisterEditor(...)中,我们发现该方法的内部实现与自定义属性编辑器实现方式基本一样,即:通过调用registry.registerCustomEditor(requiredType, editor)方法来注册属性编辑器。如下所示:

此处我们再提及一点,就是在PropertyEditorRegistrySupport类的createDefaultEditors()方法中,包含了Spring给我们提供的一系列默认编辑器。具体如下所示:

private void createDefaultEditors() {
    this.defaultEditors = new HashMap<>(64);
    
    // Simple editors, without parameterization capabilities.
    this.defaultEditors.put(Charset.class, new CharsetEditor());
    this.defaultEditors.put(Class.class, new ClassEditor());
    this.defaultEditors.put(Class[].class, new ClassArrayEditor());
    this.defaultEditors.put(Currency.class, new CurrencyEditor());
    this.defaultEditors.put(File.class, new FileEditor());
    this.defaultEditors.put(InputStream.class, new InputStreamEditor());
    if (!shouldIgnoreXml) 
        this.defaultEditors.put(InputSource.class, new InputSourceEditor());
    this.defaultEditors.put(Locale.class, new LocaleEditor());
    this.defaultEditors.put(Path.class, new PathEditor());
    this.defaultEditors.put(Pattern.class, new PatternEditor());
    this.defaultEditors.put(Properties.class, new PropertiesEditor());
    this.defaultEditors.put(Reader.class, new ReaderEditor());
    this.defaultEditors.put(Resource[].class, new ResourceArrayPropertyEditor());
    this.defaultEditors.put(TimeZone.class, new TimeZoneEditor());
    this.defaultEditors.put(URI.class, new URIEditor());
    this.defaultEditors.put(URL.class, new URLEditor());
    this.defaultEditors.put(UUID.class, new UUIDEditor());
    this.defaultEditors.put(ZoneId.class, new ZoneIdEditor());
    
    // Default instances of collection editors.
    this.defaultEditors.put(Collection.class, new CustomCollectionEditor(Collection.class));
    this.defaultEditors.put(Set.class, new CustomCollectionEditor(Set.class));
    this.defaultEditors.put(SortedSet.class, new CustomCollectionEditor(SortedSet.class));
    this.defaultEditors.put(List.class, new CustomCollectionEditor(List.class));
    this.defaultEditors.put(SortedMap.class, new CustomMapEditor(SortedMap.class));
    
    // Default editors for primitive arrays.
    this.defaultEditors.put(byte[].class, new ByteArrayPropertyEditor());
    this.defaultEditors.put(char[].class, new CharArrayPropertyEditor());
    
    // The JDK does not contain a default editor for char!
    this.defaultEditors.put(char.class, new CharacterEditor(false));
    this.defaultEditors.put(Character.class, new CharacterEditor(true));
    
    // Spring's CustomBooleanEditor accepts more flag values than the JDK's default editor.
    this.defaultEditors.put(boolean.class, new CustomBooleanEditor(false));
    this.defaultEditors.put(Boolean.class, new CustomBooleanEditor(true));
    
    // The JDK does not contain default editors for number wrapper types!
    // Override JDK primitive number editors with our own CustomNumberEditor.
    this.defaultEditors.put(byte.class, new CustomNumberEditor(Byte.class, false));
    this.defaultEditors.put(Byte.class, new CustomNumberEditor(Byte.class, true));
    this.defaultEditors.put(short.class, new CustomNumberEditor(Short.class, false));
    this.defaultEditors.put(Short.class, new CustomNumberEditor(Short.class, true));
    this.defaultEditors.put(int.class, new CustomNumberEditor(Integer.class, false));
    this.defaultEditors.put(Integer.class, new CustomNumberEditor(Integer.class, true));
    this.defaultEditors.put(long.class, new CustomNumberEditor(Long.class, false));
    this.defaultEditors.put(Long.class, new CustomNumberEditor(Long.class, true));
    this.defaultEditors.put(float.class, new CustomNumberEditor(Float.class, false));
    this.defaultEditors.put(Float.class, new CustomNumberEditor(Float.class, true));
    this.defaultEditors.put(double.class, new CustomNumberEditor(Double.class, false));
    this.defaultEditors.put(Double.class, new CustomNumberEditor(Double.class, true));
    this.defaultEditors.put(BigDecimal.class, new CustomNumberEditor(BigDecimal.class, true));
    this.defaultEditors.put(BigInteger.class, new CustomNumberEditor(BigInteger.class, true));
    
    // Only register config value editors if explicitly requested.
    if (this.configValueEditorsActive) {
        StringArrayPropertyEditor sae = new StringArrayPropertyEditor();
        this.defaultEditors.put(String[].class, sae);
        this.defaultEditors.put(short[].class, sae);
        this.defaultEditors.put(int[].class, sae);
        this.defaultEditors.put(long[].class, sae);
    }
}

4.4> 添加ApplicationContextAwareProcessor处理器

下面我们继续来分析prepareBeanFactory(...)方法中的源码,如下所示:

解释】在上面的源码红框中,只是将ApplicationContextAwareProcessor实例对象添加到了beanFactory的后置处理器集合中(即:List<BeanPostProcessor> beanPostProcessors)。

那既然ApplicationContextAwareProcessor是后置处理器,那它必然就已经实现了BeanPostProcessor接口。那么问题来了,后置处理器会在什么时候被Spring调用呢? 这个问题在前面章节的 bean实例化 过程中其实已经介绍过了,我们再来回顾一下。请见下图所示:

解释】在bean实例化的时候,也就是在Spring调用init-method方法的前后,会分别调用所有实现了BeanPostProcessor接口的实现类的postProcessBeforeInitialization(...)方法和postProcessAfterInitialization(...)方法。那么,下面我们就来看一下ApplicationContextAwareProcessor是如何自定义这两个方法的具体逻辑的。

ApplicationContextAwareProcessorpostProcessAfterInitialization(...)方法没有做特殊逻辑处理,采用的就是它父接口BeanPostProcessor中默认的方法实现。源码如下所示:

default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    return bean;
}

ApplicationContextAwareProcessorpostProcessBeforeInitialization(...)方法做了自定义的实现,源码如下所示:

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    // 只针对如下7种xxxAware进行特殊处理
    if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
            bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
            bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware ||
            bean instanceof ApplicationStartupAware)) return bean;
    ... ...
    invokeAwareInterfaces(bean); // 为7种xxxAware设置对应的资源
    ... ...
    return bean;
}
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 ApplicationStartupAware) 
        ((ApplicationStartupAware) bean).setApplicationStartup(this.applicationContext.getApplicationStartup());
    if (bean instanceof ApplicationContextAware) 
        ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}

解释】通过上面的源码,我们可以发现,在后置处理方法中,只是针对7种Aware实现设置了它们所需的资源。

4.5> 设置忽略依赖

我们继续来分析prepareBeanFactory(...)方法中如下红框的内容。在4.3中我们当通过调用ApplicationContextAwareProcessor类的invokeAwareInterfaces()方法之后,7种xxxAware类型bean就都已经处理完毕了,那么下面的依赖注入操作就不需要再处理这些xxxAware类了。所以在如下红框种就对这7种Aware类执行了忽略依赖操作。

4.6> 注册依赖

下面我们再来继续分析prepareBeanFactory(...)方法中如下红框的内容。这部分内容是,当注册了依赖解析后,例如当注册了对BeanFactory.class的解析依赖后,当bean的属性注入的时候,一旦检测到属性为BeanFactory类型便会将beanFactory的实例注入进去。

五、invokeBeanFactoryPostProcessors(...)激活各种BeanFactory的后置处理器

下面我们来分析refresh()方法中的invokeBeanFactoryPostProcessors(beanFactory)这段代码的含义之前,通过方法名称,我们可以猜到这段方法是用来处理BeanFactoryPostProcessor的,那么 它具体是做什么的呢? 请见如下源码所示:

@FunctionalInterface
public interface BeanFactoryPostProcessor {
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

解释BeanFactoryPostProcessor接口跟BeanPostProcessor类似,都可以对bean的定义(即:配置的元数据)进行处理。也就是说,Spring允许BeanFactoryPostProcessor在容器实际实例化任何其他的bean之前读取配置元数据,并进行修改。如果配置多个BeanFactoryPostProcessor,可以通过实现Ordered接口来控制执行次序。

如果你想改变实际的bean实例,那么最好使用BeanPostProcessor。因为BeanFactoryPostProcessor的作用域范围是容器级别的。它只和你所使用的容器有关。它不会对定义在另一个容器中的bean进行后置处理。

那么,我们先不着急解析invokeBeanFactoryPostProcessors(beanFactory)这段源码的具体逻辑,我们先插播一条“娱乐新闻”,即:如何去使用BeanFactoryPostProcessor

5.1> PropertySourcesPlaceholderConfigurer的应用

在某个路径下创建一个配置文件,我们以prop/common.properties为例,在该文件内配置相应的属性值。

然后创建message的Bean配置信息,变量引用:${message.msg}。表明在其他的配置文件中指定了message.msg的值。那到底是哪个配置文件呢?我们创建PropertySourcesPlaceholderConfigurer的Bean配置信息。通过locations属性,来指定配置文件所在路径。

从IOC中获取message的bean实例对象,然后输出msg的值。

在上面的演示中,我们可以看到虽然在配置PropertySourcesPlaceholderConfigurer的Bean时指定了common.properties的文件路径,但是这个文件是什么时候被加载解析的呢? 答案是:PropertySourcesPlaceholderConfigurer其实就是一种BeanFactoryPostProcessor,那么当Spring加载任何实现了这个接口的bean的配置时,都会在bean工厂载入所有bean的配置之后执行postProcessBeanFactory(beanFactory)方法。类的继承关系图请见下图所示:

本节开篇就说了,BeanFactoryPostProcessor接口只有一个方法,即:postProcessBeanFactory(beanFactory),那么下面我们来看一下PropertySourcesPlaceholderConfigurer类是怎么实现的这个方法:

那么当配置文件信息被加载到了内存中后,就可以通过message.msg所配置的“Hello World!”值来替换${message.msg}了。此处替换流程暂且不深究。下面我们来看看如何自己实现一个BeanFactoryPostProcessor。

5.2> 自定义BeanFactoryPostProcessor的应用

实现接口BeanFactoryPostProcessor,创建自定义处理类。在postProcessBeanFactory(beanFactory)方法中实现垃圾话过滤逻辑。需要注意的是,这个过滤垃圾话的作用域是针对容器内所有的bean的

配置Bean,设置需要过滤的垃圾话列表。

获取allMessage的bean实例,验证msg中的垃圾话是否被过滤掉了。

5.3> 激活BeanFactoryPostProcessor源码解析

5.3.1> 源码注释解析

通过上面的示例,我们基本了解到了BeanFactoryPostProcessor是如何使用的。那么,下面我们还是回到refresh()方法中,看一下invokeBeanFactoryPostProcessors(beanFactory)这段代码的具体实现逻辑:

invokeBeanFactoryPostProcessors(beanFactory)方法的源码如下所示:

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    // 激活BeanFactoryPostProcessor操作
    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
    
    if (!NativeDetector.inNativeImage() && beanFactory.getTempClassLoader() == null 
        && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
        beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
        beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }
}

激活的实际逻辑代码就在invokeBeanFactoryPostProcessors(...)方法中,里面逻辑很多,我们先看这个方法的源码和注释,然后我们在下面内容中,抽取重要的内容再详细的讨论。

public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, 
                                                 List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
    Set<String> processedBeans = new HashSet<>(); // 用于存储已经处理过的处理器名称
    
    /** 步骤1:对BeanDefinitionRegistry类型进行处理,包括【硬编码】+【配置方式】 */
    if (beanFactory instanceof BeanDefinitionRegistry) {
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; 
        List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>(); // 通过【硬编码】注册的BeanFactoryPostProcessor
        List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>(); // 通过【硬编码】注册的BeanDefinitionRegistryPostProcessor

        /** 步骤1.1:遍历所有通过【硬编码】设置的BeanFactoryPostProcessor,按类型执行“分堆”操作,调用其postProcessBeanDefinitionRegistry(registry)方法 */
        for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
            if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) { 
                BeanDefinitionRegistryPostProcessor registryProcessor = (BeanDefinitionRegistryPostProcessor) postProcessor;
                registryProcessor.postProcessBeanDefinitionRegistry(registry); // BeanDefinitionRegistryPostProcessor接口中的方法,对BeanDefinitionRegistry执行后置处理
                registryProcessors.add(registryProcessor); // 通过【硬编码】注册的BeanDefinitionRegistryPostProcessor,会被放到registryProcessors中
            } else {
                regularPostProcessors.add(postProcessor); // 通过【硬编码】注册的其它BeanFactoryPostProcessor,会被放到regularPostProcessors中
            }
        }

        /** 步骤1.2:处理所有通过【配置方式】注册且实现了”PriorityOrdered接口“的BeanDefinitionRegistryPostProcessor实例集合,调用其postProcessBeanDefinitionRegistry(registry)方法 */
        List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
        String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
        for (String ppName : postProcessorNames) {
            if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { // 如果实现了PriorityOrdered接口,则进行排序
                currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                processedBeans.add(ppName);
            }
        }
        sortPostProcessors(currentRegistryProcessors, beanFactory); // 排序
        registryProcessors.addAll(currentRegistryProcessors); // 合并【硬编码】和【配置方式(且实现了PriorityOrdered接口)】的BeanDefinitionRegistryPostProcessor集合
        // 调用所有currentRegistryProcessors实例的postProcessBeanDefinitionRegistry(registry)方法,实现对registry的后置处理
        invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
        currentRegistryProcessors.clear();

        /** 步骤1.3:处理所有通过【配置方式】注册且实现了”Ordered接口“的BeanDefinitionRegistryPostProcessor实例集合,调用其postProcessBeanDefinitionRegistry(registry)方法 */
        postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
        for (String ppName : postProcessorNames) {
            if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) { // 如果实现了Ordered接口,则进行排序
                currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                processedBeans.add(ppName);
            }
        }
        sortPostProcessors(currentRegistryProcessors, beanFactory); // 排序
        registryProcessors.addAll(currentRegistryProcessors); // 合并【硬编码】和【配置方式(且实现了Ordered接口)】的BeanDefinitionRegistryPostProcessor集合
        invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
        currentRegistryProcessors.clear();

        /** 步骤1.4:处理所有通过【配置方式】注册且实现了BeanDefinitionRegistryPostProcessor实例集合(排除实现Ordered接口和PriorityOrdered接口),调用其postProcessBeanDefinitionRegistry(registry)方法 */
        boolean reiterate = true;
        while (reiterate) {
            reiterate = false;
            postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
            for (String ppName : postProcessorNames) {
                if (!processedBeans.contains(ppName)) {
                    currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                    processedBeans.add(ppName);
                    reiterate = true;
                }
            }
            sortPostProcessors(currentRegistryProcessors, beanFactory);
            registryProcessors.addAll(currentRegistryProcessors);
            invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
            currentRegistryProcessors.clear();
        }

        /** 步骤1.5:处理BeanDefinitionRegistryPostProcessor实例集合和BeanFactoryPostProcessor实例集合,调用其postProcessBeanFactory(beanFactory)方法 */
        invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
        invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
    }
        
    /** 步骤2:对ConfigurableListableBeanFactory而非BeanDefinitionRegistry类型进行处理【硬编码】+【配置方式】 ,调用其postProcessBeanFactory(beanFactory)方法*/
    else {
        invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
    }

    String[] postProcessorNames =
            beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
    List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
    List<String> orderedPostProcessorNames = new ArrayList<>();
    List<String> nonOrderedPostProcessorNames = new ArrayList<>();
    for (String ppName : postProcessorNames) {
        if (processedBeans.contains(ppName)) // skip - already processed in first phase above
        else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) // 如果实现了PriorityOrdered接口,则进行排序
            priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
        else if (beanFactory.isTypeMatch(ppName, Ordered.class)) // 如果实现了Ordered接口,则进行排序
            orderedPostProcessorNames.add(ppName);
        else 
            nonOrderedPostProcessorNames.add(ppName); // 否则,不排序了
    }

    /** 步骤3:对ConfigurableListableBeanFactory类型并且实现了”PriorityOrdered接口“的进行处理【配置方式】 ,调用其postProcessBeanFactory(beanFactory)方法*/
    sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
    invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
    List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
    for (String postProcessorName : orderedPostProcessorNames) {
        orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
    }

    /** 步骤4:对ConfigurableListableBeanFactory类型并且实现了”Ordered接口“的进行处理【配置方式】 ,调用其postProcessBeanFactory(beanFactory)方法*/
    sortPostProcessors(orderedPostProcessors, beanFactory);
    invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
    List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
    for (String postProcessorName : nonOrderedPostProcessorNames) {
        nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
    }

    /** 步骤5:对ConfigurableListableBeanFactory类型并且没有实现(PriorityOrdered接口或Ordered接口)进行处理【配置方式】 ,调用其postProcessBeanFactory(beanFactory)方法*/
    invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
    beanFactory.clearMetadataCache();
}

通过上面的源码和源码注释,我们可以看到,其实就是针对两种情况进行分别的后置处理:

情况1】一个类实现了ConfigurableListableBeanFactory接口 + BeanDefinitionRegistry接口
情况2】一个类仅实现了ConfigurableListableBeanFactory接口

而对于后置处理器来说,有如下对应的处理关系:

针对BeanDefinitionRegistry接口】我们采用BeanDefinitionRegistryPostProcessor后置处理器进行增强操作。
针对ConfigurableListableBeanFactory接口】我们采用BeanFactoryPostProcessor后置处理器进行增强操作。

而由于BeanDefinitionRegistryPostProcessor继承了BeanFactoryPostProcessor,所以要区别处理:

5.3.2> 整体流程概述

找出实现了BeanDefinitionRegistry接口 + ConfigurableListableBeanFactory接口的bean

步骤1】调用“硬编码”设置的BeanDefinitionRegistryPostProcessorpostProcessBeanDefinitionRegistry(registry)方法进行增强;
步骤2】调用“配置方式”设置的BeanDefinitionRegistryPostProcessor并实现了PriorityOrdered接口的postProcessBeanDefinitionRegistry(registry)方法进行增强;
步骤3】调用“配置方式”设置的BeanDefinitionRegistryPostProcessor并实现了Ordered接口的postProcessBeanDefinitionRegistry(registry)方法进行增强;
步骤4】调用“配置方式”设置的BeanDefinitionRegistryPostProcessor没有实现排序接口的postProcessBeanDefinitionRegistry(registry)方法进行增强;
步骤5】调用“硬编码BeanDefinitionRegistryPostProcessorpostProcessBeanFactory(beanFactory)方法进行增强;
步骤6】调用“硬编码BeanFactoryPostProcessorpostProcessBeanFactory(beanFactory)方法进行增强;

找出仅仅实现了ConfigurableListableBeanFactory接口的bean

步骤1】调用“硬编码”设置BeanFactoryPostProcessorpostProcessBeanFactory(beanFactory)方法进行增强;
步骤2】调用“配置方式”设置的BeanFactoryPostProcessor并实现了PriorityOrdered接口的postProcessBeanFactory(beanFactory)方法进行增强;
步骤3】调用“配置方式”设置的BeanFactoryPostProcessor并实现了Ordered接口的postProcessBeanFactory(beanFactory)方法进行增强;
步骤4】调用“配置方式”设置的BeanFactoryPostProcessor没有实现排序接口的postProcessBeanFactory(beanFactory)方法进行增强;

那么,什么样的类即实现BeanDefinitionRegistry接口又实现ConfigurableListableBeanFactory接口呢? 我们以DefaultListableBeanFactory为例,它就是既实现了ConfigurableListableBeanFactory接口,也实现了BeanDefinitionRegistry接口。如下所示:

那么 上面所说的【硬编码】,又是从哪里写入的呢? 我们可以看到在 AbstractApplicationContext 类中有addBeanFactoryPostProcessor(postProcessor)方法,通过它,我们就可以采用硬编码的方式添加PostProcessor了。

为了更便于大家对整个流程的理解,如下我提供了针对整体处理流程的图解。

5.3.3> 整体流程图解

针对实现了BeanDefinitionRegistry接口 + ConfigurableListableBeanFactory接口 的bean,调用BeanDefinitionRegistryPostProcessor 类的postProcessBeanDefinitionRegistry(registry)方法的后置处理

针对实现了BeanDefinitionRegistry接口 + ConfigurableListableBeanFactory接口 的bean,调用 BeanFactoryPostProcessor 类的postProcessBeanFactory(beanFactory)方法的后置处理

针对仅仅实现了ConfigurableListableBeanFactory接口的bean,调用BeanFactoryPostProcessor类的postProcessBeanFactory(beanFactory)方法的后置处理

六、registerBeanPostProcessors(...)注册各种Bean的后置处理器

6.1> 使用示例

创建一个BeanPostProcessor的实现类——MuseBeanPostProcessor.java

oldbean.xml配置文件中注册MuseBeanPostProcessor后置处理器。

启动项目,我们发现当使用ApplicationContext加载bean的时候,就可以调用我们创建的后置处理器;但是如果使用XmlBeanFactory加载bean,却无法调用我们创建的后置处理器

其实这也印证了ApplicationContext是BeanFactory的加强版本,即:Plus版本。在Spring中,其实绝大部分的功能都是通过后置处理器的方式进行扩展的,而BeanFactory其实只是初始化IOC容器了,并没有实现后置处理器的自动注册功能。

而ApplicationContext实现了自动添加后置处理器的能力,所以才会通过调用applicationContext.getBean("allMessage")的方式输出了“-------allMessage---------”,而通过beanFactory.getBean("allMessage")的方式却没有输出。

6.2> 源码解析

我们了解了怎样去使用BeanPostProcessor之后,下面我们来看一下涉及到注册BeanPostProcessor的逻辑代码,如下所示:

如下是最核心的BeanPostProcessor方法,这里面的逻辑与BeanFactoryPostProcessor类似,请见如下源码和注释:

public static void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
    String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
    
    // BeanPostProcessorChecker是一个用于检查操作的后置处理器,即:当Spring配置中的后处理器还没有被注册就已经开始了bean的初始化是,那么Checker就会打印提示信息日志
    int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
    beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));

    List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
    List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
    List<String> orderedPostProcessorNames = new ArrayList<>();
    List<String> nonOrderedPostProcessorNames = new ArrayList<>();
    for (String ppName : postProcessorNames) {
        if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { // 存储实现了PriorityOrdered接口的后置处理器
            BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class); 
            priorityOrderedPostProcessors.add(pp);
            if (pp instanceof MergedBeanDefinitionPostProcessor) 
                internalPostProcessors.add(pp); // 存储实现了PriorityOrdered接口并且是MergedBeanDefinitionPostProcessor类型后置处理器
        }
        else if (beanFactory.isTypeMatch(ppName, Ordered.class)) 
            orderedPostProcessorNames.add(ppName); // 存储实现了Ordered接口的后置处理器
        else 
            nonOrderedPostProcessorNames.add(ppName); // 存储未实现排序接口的后置处理器
    }
    sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
    registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);
    
    List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
    for (String ppName : orderedPostProcessorNames) {
        BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
        orderedPostProcessors.add(pp);
        if (pp instanceof MergedBeanDefinitionPostProcessor) {
            internalPostProcessors.add(pp); // 存储实现了Ordered接口并且是MergedBeanDefinitionPostProcessor类型后置处理器
        }
    }
    sortPostProcessors(orderedPostProcessors, beanFactory);
    registerBeanPostProcessors(beanFactory, orderedPostProcessors);

    List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
    for (String ppName : nonOrderedPostProcessorNames) {
        BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
        nonOrderedPostProcessors.add(pp);
        if (pp instanceof MergedBeanDefinitionPostProcessor) {
            internalPostProcessors.add(pp); // 存储没有实现排序接口并且是MergedBeanDefinitionPostProcessor类型后置处理器
        }
    }
    registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);

    // 注册MergedBeanDefinitionPostProcessor类型后置处理器
    sortPostProcessors(internalPostProcessors, beanFactory);
    registerBeanPostProcessors(beanFactory, internalPostProcessors);
    
    beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
}

步骤1】针对实现了PriorityOrdered接口的BeanPostProcessor后置处理器执行排序和注册操作。
步骤2】针对实现了Ordered接口的BeanPostProcessor后置处理器执行排序和注册操作。
步骤3】针对没有实现排序接口的BeanPostProcessor后置处理器执行注册操作。
步骤4】针对MergedBeanDefinitionPostProcessor类型的后置处理器执行注册操作。

在调用registerBeanPostProcessors(...)方法时,不用担心重复注册问题,因为都是先执行remove再执行add的,如下所示:

七、initMessageSource()为上下文初始化消息源

Spring国际化是根据客户端的系统语言类型返回对应的界面,这个便是所谓的i18n国际化(internationalization)。

国际化信息也称之为本地化信息,需要两个条件来最终决定:

条件1】语言类型(eg:中文)
条件2】国家/地区的类型(ge:中国大陆、中国台湾、中国香港、新加坡、马来西亚……)

在JDK中,提供了java.util.Locale类用来支持国际化信息,它提供了如下使用方式:

方式1】指定语言和国家/地区——new Locale("zh", "CN") 或 Locale.CHINA
方式2】指定语言——new Locale("zh") 或 Locale.CHINESE
方式3】根据操作系统默认语言设置——Locale.getDefault()

MessageSource是负责国际化信息的接口,它有如下几个重要实现类:

ResourceBundleMessageSource:允许通过资源名加载国际化资源。
ReloadableResourceBundleMessageSource:与ResourceBundleMessageSource功能相似,额外提供定时刷新功能,不用重启系统,即可更新资源信息。
StaticMessageSource:主要用于程序测试,允许通过编程的方式提供国际化信息。
DelegatingMessageSource:为方便操作MessageSource的代理类。

7.1> 使用示例

创建i18n_zh.properties文件,里面添加“hello=你好!世界!”,然后通过 native2ascii 将其转化为utf8的ASCII编码文件。

创建i18n_en.properties文件,里面添加“hello=Hello World!”,然后将i18n_zh.propertiesi18n_en.properties都放入到muse文件夹下。如下所示:

在配置文件oldbean.xml中配置国际化支持,其中bean id必须是“messageSource”,否则就会报NoSuchMessageException异常

执行测试代码:

7.2> 源码解析

了解了怎么使用国际化信息之后,我们现在来看一下初始化国际化资源的代码逻辑,如下图红框所示:

那么,在initMessageSource()方法中,通过判断是自定义国际化资源还是默认国际化资源,创建MessageSource实现类,然后赋值给AbstractApplicationContext的全局变量messageSource

protected void initMessageSource() {
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    // 如果需要用户自定义国际化资源,则需要创建一个bean的id必须是“messageSource”(硬编码)的bean
    if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) { 
        this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class); // 赋值给全局变量messageSource
        if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
            HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
            if (hms.getParentMessageSource() == null) {
                hms.setParentMessageSource(getInternalParentMessageSource());
            }
        }
    }
    // 否则,使用DelegatingMessageSource,一般后续通过调用getMessage方法返回国际化资源
    else {
        DelegatingMessageSource dms = new DelegatingMessageSource();
        dms.setParentMessageSource(getInternalParentMessageSource());
        this.messageSource = dms; // 赋值给全局变量messageSource
        beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
    }
}

protected MessageSource getInternalParentMessageSource() {
    return (getParent() instanceof AbstractApplicationContext ?
        ((AbstractApplicationContext) getParent()).messageSource : getParent());
}

public ApplicationContext getParent() {
    return this.parent;
}

在使用时,我们通过调用applicationContext.getMessage("hello", null, Locale.US)来获取国际化信息,那么我们看一下getMessage(...)方法的源码是怎么处理的:

public String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException {
    return getMessageSource().getMessage(code, args, locale);
}

private MessageSource getMessageSource() throws IllegalStateException {
    if (this.messageSource == null) 
        throw new IllegalStateException("MessageSource not initialized - call 'refresh' before accessing messages via the context: " + this);
    return this.messageSource; // 就是从全局messageSource中获取的国际化资源
}

八、initApplicationEventMulticaster()为上下文初始化应用事件广播器

8.1> 使用示例

首先,继承ApplicationEvent,实现自定义ApplicationEvent——MuseApplicationEvent

其次,实现ApplicationListener接口,实现自定义ApplicationListener——MuseApplicationListener

将自定义应用监听器MuseApplicationListener注册倒IOC中:

测试发布一个MuseApplicationEvent类型的事件,查看控制台输出内容:

8.2> 源码解析

好了,我们通过刚刚的示例,了解到了如何使用应用事件发起广播,那么下面我们就来看一下源码中如何进行初始化操作的,在下图refresh()方法的红框中:

我们再来看initApplicationEventMulticaster()方法的具体处理逻辑:

protected void initApplicationEventMulticaster() {
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    // 如果用户【自定义】了EventMulticaster,则向Spring中注册用户自定义的事件广播器
    if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
        this.applicationEventMulticaster = beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
    }
    // 如果用户【没有自定义】EventMulticaster,则向Spring中注册默认的SimpleApplicationEventMulticaster广播器
    else {
        this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
        beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
    }
}

上面的代码逻辑也是比较简单清晰的,就执行了两个判断逻辑:
判断1】如果用户自定义了EventMulticaster,则向Spring中注册用户自定义的事件广播器
判断2】如果用户没有自定义EventMulticaster,则向Spring中注册默认的SimpleApplicationEventMulticaster广播器

那么,我们在看一下SimpleApplicationEventMulticaster中是怎么处理广播事件的:

public void multicastEvent(ApplicationEvent event) {
    multicastEvent(event, resolveDefaultEventType(event));
}
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    Executor executor = getTaskExecutor();
    for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
        if (executor != null) 
            executor.execute(() -> invokeListener(listener, event)); // 采用并行方式执行广播操作
        else 
            invokeListener(listener, event); // 采用串行方式执行广播操作
    }
}

protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
    ErrorHandler errorHandler = getErrorHandler();
    if (errorHandler != null) 
        try {
            doInvokeListener(listener, event); // 触发广播监听行为
        }catch (Throwable err) {errorHandler.handleError(err);}
    else 
        doInvokeListener(listener, event); // 触发广播监听行为
}

private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
    try {
        listener.onApplicationEvent(event); // 发送监听事件
    }catch (ClassCastException ex) {...}
}

从上面的代码中,我们可以看出核心的逻辑就是两个步骤:
步骤1】通过getApplicationListeners(event, type)方法获得所有的应用监听器列表。
步骤2】遍历调用每一个ApplicationListeneronApplicationEvent(event)方法来发送监听事件。

需要注意的一点就是,当产生Spring监听事件的时候,对于每一个监听器来说,其实他们都可以获得所有产生的监听事件,那么具体处理哪一种类型的监听事件,可以在监听器中进行逻辑处理(如:上面8.1使用示例中的MuseApplicationListener实现方式)。当然,我们也可以通过使用泛型来约束处理的监听事件类型。如下图所示,那么MuseApplicationListener就只能处理MuseApplicationEvent类型的事件了。

九、registerListeners()注册监听器

注册监听器的理解代码如下所示:

下面我们再来详细看一下registerListeners()方法的具体逻辑:

protected void registerListeners() {
    /** 添加以【硬编码】方式注册的监听器 */
    for (ApplicationListener<?> listener : getApplicationListeners()) 
        getApplicationEventMulticaster().addApplicationListener(listener);

    /** 添加以【配置文件】方式注册的监听器 */
    String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
    for (String listenerBeanName : listenerBeanNames) 
        getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
    
    /** 发送在多播设置之前发布的ApplicationEvent事件 */
    Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
    this.earlyApplicationEvents = null;
    if (!CollectionUtils.isEmpty(earlyEventsToProcess)) 
        for (ApplicationEvent earlyEvent : earlyEventsToProcess) 
            getApplicationEventMulticaster().multicastEvent(earlyEvent);
}

注册监听器的方法也比较简单清晰,它一共做到了如下3个步骤:
步骤1】添加以硬编码方式注册的监听器
步骤2】添加以配置文件方式注册的监听器
步骤3】发送在多播设置之前发布的ApplicationEvent事件

十、finishBeanFactoryInitialization(...)初始化非延迟加载单例

10.1> 概述

此处涉及到的就是我们完成了BeanFactory初始化工作之后的收尾工作了,包含:

步骤1】为上下文添加ConversionService
步骤2】为上下文添加EmbeddedValueResolver(嵌入值解析器)
步骤3】初始化LoadTimeWeaverware类型的Bean
步骤4】停止使用临时ClassLoader进行类型匹配
步骤5】冻结配置
步骤6】实例化所有剩余的(非惰性初始化)单例

10.2> 源码解析

我们来看一下负责这部分逻辑的代码:

具体的逻辑处理就是在finishBeanFactoryInitialization(beanFactory)的方法中,如下所示:

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
    /** 步骤1:为上下文初始化ConversionService */
    if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) && // 是否存在名称为“conversionService”并且类型是ConversionService的Bean
            beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) 
        beanFactory.setConversionService(beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));

    /** 步骤2:如果没有设置EmbeddedValueResolver(嵌入值解析器),则默认添加一个StringValueResolver */
    if (!beanFactory.hasEmbeddedValueResolver()) 
        beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
    
    /** 步骤3:尽早初始化LoadTimeWeaverware类型的Bean,以便尽早注册其转换器 */
    String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
    for (String weaverAwareName : weaverAwareNames) 
        getBean(weaverAwareName);
    
    beanFactory.setTempClassLoader(null); // 停止使用临时ClassLoader进行类型匹配
    beanFactory.freezeConfiguration(); // 冻结配置
    beanFactory.preInstantiateSingletons(); // 实例化所有剩余的(非惰性初始化)单例
}

冻结所有配置

public void freezeConfiguration() {
    this.configurationFrozen = true; // 开启配置冻结开关
    this.frozenBeanDefinitionNames = StringUtils.toStringArray(this.beanDefinitionNames);
}

初始化非延迟加载单例

public void preInstantiateSingletons() throws BeansException {
    List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
    /** 步骤1:初始化所有非惰性单例bean */
    for (String beanName : beanNames) {
        RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
        if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
            // 针对FactoryBean创建bean
            if (isFactoryBean(beanName)) {
                Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
                if (bean instanceof FactoryBean) {
                    FactoryBean<?> factory = (FactoryBean<?>) bean;
                    boolean isEagerInit;
                    if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) 
                        isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit, getAccessControlContext());
                    else 
                        isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean<?>) factory).isEagerInit());
                    if (isEagerInit) // 对于配置了【非惰性加载】的bean执行创建操作
                        getBean(beanName);
                }
            } 
            // 针对非FactoryBean创建bean
            else getBean(beanName); 
        }
    }
    /** 步骤2:对于配置了SmartInitializingSingleton的Bean,触发初始化后回调(post-initialization callback)*/
    for (String beanName : beanNames) {
        Object singletonInstance = getSingleton(beanName);
        // 针对SmartInitializingSingleton类型触发初始化后回调,即:afterSingletonsInstantiated
        if (singletonInstance instanceof SmartInitializingSingleton) {
            SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
            if (System.getSecurityManager() != null) 
                AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                    smartSingleton.afterSingletonsInstantiated(); // 触发初始化后回调
                    return null;
                }, getAccessControlContext());
            else smartSingleton.afterSingletonsInstantiated(); // 触发初始化后回调
        }
    }
}

在上面代码中,总共执行了如下两个步骤:
步骤1】步骤1:初始化所有非惰性(isEagerInit)单例bean。即:针对FactoryBean类型创建bean 和 针对其他类型创建bean;
步骤2】对于配置了SmartInitializingSingleton的Bean,触发初始化后回调,即:调用afterSingletonsInstantiated()方法;

十一、finishRefresh()完成refresh操作

最后这部分,我们来看一下finishRefresh()方法的逻辑代码:

finishRefresh()方法中,主要就是针对Lifecycle接口进行处理,该接口主要包含如下两个重要方法:

start() :Spring启动的时候,调用该方法开始生命周期;
stop() :Spring关闭的时候,调用该方法结束生命周期;

那么下面我们来看一下在finishRefresh()方法中,它是如何实现这个功能的:

protected void finishRefresh() {
    clearResourceCaches(); // 仅执行清除缓存操作:resourceCaches.clear()
    initLifecycleProcessor(); // 初始化生命周期处理器
    getLifecycleProcessor().onRefresh(); // 启动所有实现了Lifecycle接口的bean
    publishEvent(new ContextRefreshedEvent(this)); // 发布上下文已刷新的事件
    if (!NativeDetector.inNativeImage()) // 判断是不是在GraalVM虚拟机上运行
        LiveBeansView.registerApplicationContext(this);
}

初始化生命周期处理器——LifecycleProcessor

protected void initLifecycleProcessor() {
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    /** 步骤1:如果存在名字是“lifecycleProcessor”的生命周期处理器,则将其赋值给全局变量lifecycleProcessor */
    if (beanFactory.containsLocalBean(LIFECYCLE_PROCESSOR_BEAN_NAME)) {
        this.lifecycleProcessor = beanFactory.getBean(LIFECYCLE_PROCESSOR_BEAN_NAME, LifecycleProcessor.class);
        
    /** 步骤2:如果不存在,则创建默认的生命周期处理器——DefaultLifecycleProcessor,并赋值给全局变量lifecycleProcessor */
    else {
        DefaultLifecycleProcessor defaultProcessor = new DefaultLifecycleProcessor();
        defaultProcessor.setBeanFactory(beanFactory);
        this.lifecycleProcessor = defaultProcessor;
        beanFactory.registerSingleton(LIFECYCLE_PROCESSOR_BEAN_NAME, this.lifecycleProcessor);
    }
}

通过调用onRefresh()方法来启动所有实现了Lifecycle接口的bean

public void onRefresh() {
    startBeans(true); // 启动所有实现了Lifecycle接口的bean
    this.running = true;
}

private void startBeans(boolean autoStartupOnly) {
    Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans(); // 获得所有Lifecycle集合
    Map<Integer, LifecycleGroup> phases = new TreeMap<>();

    /** 步骤1:如果autoStartupOnly是false,或者是SmartLifecycle类型的bean,并且其内部方法isAutoStartup()返回了true,则会保存到phase中 */
    lifecycleBeans.forEach((beanName, bean) -> {
        if (!autoStartupOnly || (bean instanceof SmartLifecycle && ((SmartLifecycle) bean).isAutoStartup())) {
            int phase = getPhase(bean);
            phases.computeIfAbsent(
                    phase,
                    p -> new LifecycleGroup(phase, this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly)
            ).add(beanName, bean);
        }
    });

    /** 步骤2:如果phase中存在待启动的LifecycleGroup,则调用每一个start()方法 */
    if (!phases.isEmpty()) 
        phases.values().forEach(LifecycleGroup::start); // 调用LifecycleGroup的start()方法
}

最后我们发现,会调用到DefaultLifecycleProcessor的内部类LifecycleGroupstart()方法;然后在doStart()方法中最终调用了每一个实现了Lifecycle接口的子类的start()方法。

public void start() {
    if (this.members.isEmpty()) return;
    Collections.sort(this.members);
    for (LifecycleGroupMember member : this.members) 
        doStart(this.lifecycleBeans, member.name, this.autoStartupOnly); // 在Spring中,真正执行某个逻辑都是以do开头的
}

private void doStart(Map<String, ? extends Lifecycle> lifecycleBeans, String beanName, boolean autoStartupOnly) {
    Lifecycle bean = lifecycleBeans.remove(beanName);
    if (bean != null && bean != this) {
        String[] dependenciesForBean = getBeanFactory().getDependenciesForBean(beanName);
        for (String dependency : dependenciesForBean) 
            doStart(lifecycleBeans, dependency, autoStartupOnly); // 如果有嵌套,则通过递归处理
        
        if (!bean.isRunning() && (!autoStartupOnly || !(bean instanceof SmartLifecycle) || ((SmartLifecycle) bean).isAutoStartup())) {
            try {
                bean.start(); // 调用Lifecycle实现类的start()方法触发生命周期开始操作
            } catch (Throwable ex) {...}
        }
    }
}

今天的文章内容就这些了:

写作不易,笔者几个小时甚至数天完成的一篇文章,只愿换来您几秒钟的 点赞 & 分享 。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/122963.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python tkinter用iconphoto方法修改窗口标题的图片

修改Python Tkinter窗口的标题图片&#xff0c;可以使用PhotoImage、iconphoto方法。这个方法允许你设置窗口的图标。 运行结果 代码示例如下&#xff1a; import tkinter as tkroot Tk()# 加载图片&#xff0c;记住一定是要PNG图片 icon tk.PhotoImage(filephoto\\图片.pn…

为什么HTTP用得很好的,开始普及HTTPS呢?

显而易见&#xff0c;现在的HTTP早已不安全&#xff0c;当我们在浏览各个网站时会发现HTTP前面都会显示不安全&#xff0c;因为HTTP是明文传输&#xff0c;一旦电脑被植入了木马&#xff0c;木马程序就会主动周期性发消息给Internet的控制终端&#xff0c;这样NAT小洞会一直敞开…

科技创意赋能乡村文旅振兴

近日&#xff0c;由北京大学创意产业研究中心联合中国国际科技促进会新基建专委会共同主办的“科技创意赋能乡村振兴研讨会”在京举行&#xff0c;与会专家学者围绕“和美乡村共同富裕智慧文旅”主题进行深入探讨。北京大学创意产业研究中心副主任吕艺、国家文化和旅游公共服务…

Pytorch R-CNN目标检测-汽车car

概述 目标检测(Object Detection)就是一种基于目标几何和统计特征的图像分割,它将目标的分割和识别合二为一,通俗点说就是给定一张图片要精确的定位到物体所在位置,并完成对物体类别的识别。其准确性和实时性是整个系统的一项重要能力。 R-CNN的全称是Region-CNN(区域卷积神经…

jsp基本表格和简单算法表格

基本表格&#xff1b; <% page language"java" contentType"text/html; charsetUTF-8"pageEncoding"UTF-8"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd…

C# wpf 实现任意控件(包括窗口)更多拖动功能

系列文章目录 第一章 Grid内控件拖动 第二章 Canvas内控件拖动 第三章 任意控件拖动 第四章 窗口拖动 第五章 附加属性实现任意拖动 第六章 拓展更多拖动功能&#xff08;本章&#xff09; 文章目录 系列文章目录前言一、添加的功能1、任意控件MoveTo2、任意控件DragMove3、边…

Navicat的使用--mysql

表关系 数据库的操作&#xff0c;表字段的设计&#xff0c;一般都由于图形化界面工具Navicat完成。 而表中数据的增删改查&#xff0c;需要熟悉sql语句。 一对一 一对一&#xff1a;一个A对应一个B&#xff0c;一个B对应一个A 将A或B任意一张表的主键设置为外键 一对多 一…

贷款行业极难获客,怎么获取实时客户数据?

我们能想象当客户有贷款需求时会发生什么吗&#xff1f; 客户可能会打开手机搜索“如何借款”、“哪笔贷款利息低”、“最多能借多少钱”、“贷款需要什么条件”等关键词&#xff0c;然后&#xff0c;客户点击进入第一个链接&#xff0c;然后填写他们的姓名和电话号码来测试他…

fmx windows 下 制作无边框窗口最小化最大化并鼠标可拖移窗口

1,最顶端 放一个rectangle 置顶 ,此区域后面实现鼠标拖动 移动窗口,可在上面放置最大,最小,关闭按钮 2,窗口边框模式 设置 none 3,rectangel mousemove事件 uses Winapi.Windows,Winapi.Messages,FMX.Platform.Winprocedure TfrmMain.Rectangle1MouseMove(Sender: TObje…

下拉列表框Spinner

在XML文件中的创建 <Spinnerandroid:id"id/spinner"android:layout_width"wrap_content"android:layout_height"wrap_content"/> 在Java文件中的设置 //获取Spinner对象 Spinner spinnerfindViewById(R.id.spinner); //创建数组…

经典OJ题:随机链表的复制

目录 题目&#xff1a; 本题的解图关键在于画图与看图&#xff01; 思路分析&#xff1a; 方法一&#xff1a;暴力求解法。 方法二&#xff1a;插入法 方法解析&#xff1a; 步骤一、插入 步骤二、 处理每一个copy的randdom指针⭐————重点 步骤三、拆卸节点 代码…

剑指offer全集系列Java版本(2)

目录 反转链表 替换空格 二叉树 链表的中间结点 附录 StringBuffer类中常用的方法 反转链表 反转链表_牛客题霸_牛客网 (nowcoder.com)https://www.nowcoder.com/practice/75e878df47f24fdc9dc3e400ec6058ca?tpId265&tqId39226&rp1&ru/exam/oj/ta&qru…

思谋科技进博首秀:工业多模态大模型IndustryGPT V1.0正式发布

大模型技术正在引领新一轮工业革命&#xff0c;但将其应用于工业制造&#xff0c;仍面临许多挑战&#xff0c;专业知识的缺乏是关键难点。11月5日&#xff0c;香港中文大学终身教授、思谋科技创始人兼董事长贾佳亚受邀参加第六届中国国际进口博览会暨虹桥国际经济论坛开幕式。虹…

SAP 使用函数创建多个备选BOM ( 改造标准函数 : CSAP_MAT_BOM_MAINTAIN 和 CSAP_MAT_BOM_CREATE )

参考博客1&#xff1a;https://blog.csdn.net/Buffalo_soldier/article/details/117956986 参考博客2&#xff1a;https://blog.csdn.net/u014535256/article/details/111539629 RFC CSAP_MAT_BOM_MAINTAIN 改造 SAP标准函数CSAP_MAT_BOM_MAINTAIN可以增删改BOM&#xff0c;但是…

Android JVM内存模型——老生常谈

jvm简介 JVM是Java Virtual Machine&#xff08;Java虚拟机&#xff09;的缩写&#xff0c;JVM是一种用于计算设备的规范&#xff0c;它是一个虚构出来的计算机&#xff0c;是通过在实际的计算机上仿真模拟各种计算机功能来实现的。 jvm作用 Java中的所有类&#xff0c;必须…

循环链表的设计与基本操作的实现

目录 一.循环链表的设计 二.循环链表的实现 三.循环链表的总结 一.循环链表的设计 1.循环链表的结构设计: typedef struct CNode{int data;struct CNode* next;}CNode ,*CList; 2.循环链表的示意图: 3.循环链表和单链表的区别: 唯一区别,没有空指针,尾节点的后继为头,为循…

OpenAI开发者大会之后,当何去何从?

简介 过往总结 ​产品升级 GPT-4 Turbo Agent化 此间的未来 定制GPT GPT商店 Assistants API 总结与思考 简介 此次发布会简单总结如下。 1. 发布GPT-4 Turbo&#xff1a; 更长。支持128K上下文输入&#xff0c;标准GPT-4是8K版本&#xff0c;之前升级出了32K版本 更…

TensorFlow学习笔记--(2)张量的常用运算函数

张量的取值函数 求张量的平均值: tf.reduce.mean(%张量名%)求张量的最小值:tf.reduce_min(%张量名%)求张量的最大值:tf.reduce_max(%张量名%)求张量的和:tf.reduce_sum(%张量名%)其次,对于上述所有操作 都可在函数后添加一个新的参数 axis%维度% axis0 代表第一维度 axis1 代表…

文件加密软件怎么用(附2种解密破解工具)

有时候出差或者有些商务场合&#xff0c;需要对一些敏感文件做一下简单的加密&#xff0c;这样在分享内容的时候&#xff0c;可以起到初步的保护作用。 当然了&#xff0c;如果文件非常重要&#xff0c;涉及到一些商业机密&#xff0c;这个时候你需要使用专业的加密工具&#x…