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

news2024/11/26 3:42:58

一、概述

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) {...}
        }
    }
}

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

我的月光宝盒初体验失败了

哈哈哈&#xff0c;我爱docker, docker 使我自由&#xff01;&#xff01;&#xff01; docker make me free! 菠萝菠萝蜜口号喊起来。 https://github.com/vivo/MoonBox/ windows上安装好了docker之后&#xff0c;docker-compose是自带的。 docker-compose -f docker-compo…

收单外包机构评级等级为何获D、E级及其影响分析

孟凡富 中国支付清算协会发布2022年度收单外包服务机构评级等级。本次评级工作&#xff0c;共有包括银行和非银行支付机构在内的134家收单机构对13000家外包机构进行了评价&#xff0c;参评外包机构数量较上一年度增长35.59%&#xff0c;评级工作覆盖面继续扩大。评级等级在C级…

Win10 开机突然不断重复诊断和自动修复,安全模式也进不了,如何解决?(已解决)

环境&#xff1a; Win10专业版 惠普 480G7 问题描述&#xff1a; Win10 开机突然不断重复诊断和自动修复&#xff0c;安全模式也进不了&#xff0c;如何解决&#xff1f; 修复失败&#xff0c;安全模式也是自动修复 解决方案&#xff1a; 1.尝试进入安全模式和禁用驱动模式…

Angular 由一个bug说起之一:List / Grid的性能问题

在angular中&#xff0c;MatTable构建简单&#xff0c;使用范围广。但某些时候会出现卡顿 卡顿情景&#xff1a; 1&#xff1a;一次性请求太多的数据 2&#xff1a;一次性渲染太多数据&#xff0c;这会花费CPU很多时间 3&#xff1a;行内嵌套复杂的元素 4&#xff1a;使用过多的…

第三阶段第二章——Python高阶技巧

时间过得很快&#xff0c;这么快就来到了最后一篇Python基础的学习了。话不多说直接进入这最后的学习环节吧&#xff01;&#xff01;&#xff01; 期待有一天 春风得意马蹄疾&#xff0c;一日看尽长安花 o(*&#xffe3;︶&#xffe3;*)o 1.闭包 什么是闭包&#xff1f; 答…

Postgresql数据类型-字符类型

PostgreSQL支持的字符类型如表所示。 character varying(n)存储的是变长字符类型&#xff0c;n是一个正整数&#xff0c;如果存储的字符串长度超出n则报错&#xff1b;如果存储的字符串长度比n小&#xff0c;character varying(n)仅存储字符串的实际位数。character(n)存储定长…

DDoS攻击剧增,深入解析抗DDoS防护方案

当下DDoS攻击规模不断突破上限&#xff0c;攻击方式越发复杂。面对复杂的攻击形式&#xff0c;对于企业和组织来说无疑需要更完备的抗DDoS方案&#xff0c;依靠传统的解决方法并不能做到一劳永逸。在服务器抵抗DDoS防护上&#xff0c;你不会忽略F5的产品&#xff0c;让我们一起…

aws亚马逊:什么是 Amazon EC2?

Amazon Elastic Compute Cloud&#xff08;Amazon EC2&#xff09;在 Amazon Web Services&#xff08;AWS&#xff09;云中按需提供可扩展的计算容量。使用 Amazon EC2 可以降低硬件成本&#xff0c;因此您可以更快地开发和部署应用程序。您可以使用 Amazon EC2 启动所需数量的…

金融帝国实验室(Capitalism Lab)官方正版游戏『最新销售政策』

「金融帝国实验室」&#xff08;Capitalism Lab&#xff09;Enlight 官方正版游戏「2023双11特卖」 ■优惠时限&#xff1a;2023.11.01&#xff5e;11.30 ■游戏开发商&#xff1a;Enlight Software Ltd. 请您认准以下官方正版游戏购买链接&#xff1a;支持“支付宝&am…

OPC DA客户端工具Opc quick client使用

OPC DA客户端工具Opc quick client使用 什么是OPC OPC是工业控制和生产自动化领域中使用的硬件和软件的接口标准&#xff0c;以便有效地在应用和过程控制设备之间读写数据。O代表OLE(对象链接和嵌入)&#xff0c;P (process过程)&#xff0c;C (control控制)。 OPC服务器包括…

【MySQL】表的增删改查(强化)

作者主页&#xff1a;paper jie_博客 本文作者&#xff1a;大家好&#xff0c;我是paper jie&#xff0c;感谢你阅读本文&#xff0c;欢迎一建三连哦。 本文录入于《MySQL》专栏&#xff0c;本专栏是针对于大学生&#xff0c;编程小白精心打造的。笔者用重金(时间和精力)打造&a…

一分钟秒懂人工智能对齐

文章目录 1.什么是人工智能对齐2.为什么要研究人工智能对齐3.人工智能对齐的常见方法 1.什么是人工智能对齐 人工智能对齐&#xff08;AI Alignment&#xff09;指让人工智能的行为符合人的意图和价值观。 人工智能系统可能会出现“不对齐”&#xff08;misalign&#xff09;…

Linux--gcc与make

文章目录 gcc/g的使用背景知识gcc与ggcc的编译过程预处理编译汇编链接 函数库自动化构建工具--make三个时间伪目标文件其他表示方法mybin的推导过程 gcc/g的使用 背景知识 GCC是一个开源的编译器套件&#xff0c;支持多种编程语言&#xff0c;并提供了广泛的语言特性和标准库…

真的设计师做图只需要一个炫云客户端就够了

真的设计师做图只需要一个炫云客户端就够了&#xff0c;为什么这么说呢&#xff1f;因为炫云的这个客户端功能真的太全了&#xff0c;设计师想要的功能在炫云客户端上都有&#xff0c;而且还很多功能是免费的&#xff0c;非常的实用&#xff0c;具体有哪些功能我们一起来看看吧…

无人机航迹规划:五种最新智能优化算法(SWO、COA、LSO、GRO、LO)求解无人机路径规划MATLAB

一、五种算法&#xff08;SWO、COA、LSO、GRO、LO&#xff09;简介 1、蜘蛛蜂优化算法SWO 蜘蛛蜂优化算法&#xff08;Spider wasp optimizer&#xff0c;SWO&#xff09;由Mohamed Abdel-Basset等人于2023年提出&#xff0c;该算法模型雌性蜘蛛蜂的狩猎、筑巢和交配行为&…

一键批量转码:将MP4视频转为MP3音频的简单方法

随着数字媒体设备的普及&#xff0c;视频和音频格式转换的需求也越来越常见。其中&#xff0c;将MP4视频批量转换为MP3音频的需求尤为普遍。无论是为了提取视频中的背景音乐&#xff0c;还是为了在手机或电脑上方便地收听视频音频&#xff0c;这个过程都变得非常重要。接下来我…

数字滤波器设计---FIR 滤波器设计

数字滤波器设计---FIR 滤波器设计 FIR 滤波器与 IIR 滤波器的比较 与无限持续时间冲激响应 (IIR) 滤波器相比&#xff0c;具有有限持续时间冲激响应的数字滤波器&#xff08;全零或 FIR 滤波器&#xff09;既有优点又有缺点。 FIR 滤波器具有以下主要优点&#xff1a; 它们可…

Java中的反射机制

获取字节码文件对象的三种方式 1&#xff0c;&#xff08;常用&#xff09;源代码阶段&#xff0c;Class.forName("全类名") 2&#xff0c;&#xff08;传参&#xff09;加载阶段 类名.class 3&#xff0c;&#xff08;前提有对象&#xff09;运行阶段 对象.getClas…

LeetCode算法心得——全排列(回溯型排列)

大家好&#xff0c;我是晴天学长&#xff0c;排列型的回溯&#xff0c;需要的小伙伴可以关注支持一下哦&#xff01;后续会继续更新的。&#x1f4aa;&#x1f4aa;&#x1f4aa; 1) .全排列 给定一个不含重复数字的数组 nums &#xff0c;返回其 所有可能的全排列 。你可以 按…

外贸企业GMS认证|SD-WAN专线解决方案支持 IPv6、IPv4

IP地址是英文internet protocol的缩写&#xff0c;是网络之间互连的协议。互联网诞生后&#xff0c;很长一段时间都是使用v4版本的IP协议&#xff0c;也就是 IPv4 &#xff0c;目前全球使用互联网的人数达到了48.8亿&#xff0c;而IPv4的地址库总共约43亿个地址&#xff0c;每个…