spring源码深度解析---容器的功能扩展(下)

spring源码深度解析---容器的功能扩展(上) spring源码深度解析—容器的功能扩展(上) 在之前的博文中我们一直以BeanFactory接口以及它的默认实现类XmlBeanFactory为例进行分析,但是Spring中还提供了另一个接口ApplicationContext,用于扩展BeanFactory中现有的功能。 ApplicationContext和BeanFactory两者都是用于加载Bean的,但是相比之下,Applicat... 阅读详情

spring源码深度解析—容器的功能扩展(下)

接容器的功能扩展(上)

6. BeanFactory的后处理

BeanFactory作为spring中容器功能的基础,用于存放所有已经加载的bean,为例保证程序上的高可扩展性,spring针对BeanFactory做了大量的扩展,比如我们熟悉的PostProcessor就是在这里实现的。接下来我们就深入分析下BeanFactory后处理

6.1 激活注册的BeanFactoryPostProcessor

在正是介绍BeanFactoryPostProcessor的后处理前我们先简单的了解下其用法,BeanFactoryPostProcessor接口跟BeanPostProcessor类似,都可以对bean的定义(配置元数据)进行处理,也就是说spring IoC容器允许BeanFactoryPostProcessor在容器实际实例化任何其他的bean之前读取配置元数据,并可能修改他。也可以配置多个BeanFactoryPostProcessor,可以通过order属性来控制BeanFactoryPostProcessor的执行顺序(此属性必须当BeanFactoryPostProcessor实现了Ordered的接口时才可以赊账,因此在实现BeanFactoryPostProcessor时应该考虑实现Ordered接口)。
如果想改变世界的bean实例(例如从配置元数据创建的对象),那最好使用BeanPostProcessor。同样的BeanFactoryPostProcessor的作用域范围是容器级别的,它只是和你锁使用的容器有关。如果你在容器中定义了一个BeanFactoryPostProcessor,它仅仅对此容器中的bean进行后置处理。BeanFactoryPostProcessor不会对定义在另一个容器中的bean进行后置处理,即使这两个容器都在同一层次上。在spring中存在对于BeanFactoryPostProcessor的典型应用,如PropertyPlaceholderConfigurer。

6.1.1 BeanFactoryPostProcessor的典型应用:PropertyPlaceholderConfigurer

有时候我们在阅读spring的配置文件中的Bean的描述时,会遇到类似如下情况:

    <bean id="user" class="com.yhl.myspring.demo.applicationcontext.User">
        <property name="name" value="${user.name}"/>
        <property name="birthday" value="${user.birthday"/>
    </bean>

这其中出现了变量: user.name、 u s e r . n a m e 、 {user.birthday},这是spring的分散配置,可以在另外的配置文件中为user.name、user.birthday指定值,例如在bean.properties文件中定义:

user.name = xiaoming
user.birthday = 2018-04-19

当访问名为user的bean时,其name属性就会被字符串xiaoming替换,那spring框架是怎么知道存在这样的配置文件呢,这个就是PropertyPlaceholderConfigurer,需要在配置文件中添加一下代码:

<bean id="userHandler" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations">
        <list>
            <value>classpath:bean.properties</value>
        </list>
    </property>
</bean>

在这个bean中指定了配置文件的位置。其实还是有个问题,这个userHandler只不过是spring框架管理的一个bean,并没有被别的bean或者对象引用,spring的beanFactory是怎么知道这个需要从这个bean中获取配置信息呢?我们看下PropertyPlaceholderConfigurer这个类的层次结构,如下图:
这里写图片描述
从上图中我们可以看到PropertyPlaceholderConfigurer间接的继承了BeanFactoryPostProcessor接口,这是一个很特别的接口,当spring加载任何实现了这个接口的bean的配置时,都会在bean工厂载入所有bean的配置之后执行postProcessBeanFactory方法。在PropertyResourceConfigurer类中实现了postProcessBeanFactory方法,在方法中先后调用了mergeProperties、convertProperties、processProperties这三个方法,分别得到配置,将得到的配置转换为合适的类型,最后将配置内容告知BeanFactory。
正是通过实现BeanFactoryPostProcessor接口,BeanFactory会在实例化任何bean之前获得配置信息,从而能够正确的解析bean描述文件中的变量引用。

6.1.2 自定义BeanFactoryPostProcessor

编写实现了BeanFactoryPostProcessor接口的MyBeanFactoryPostProcessor的容器后处理器,如下代码:

public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("对容器进行后处理。。。。");
    }
}

然后在配置文件中注册这个bean,如下:

<bean id="myPost" class="com.yhl.myspring.demo.applicationcontext.MyBeanFactoryPostProcessor"></bean>

最后编写测试代码:

public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        User user = (User)context.getBean("user");
        System.out.println(user.getName());

    }
}

6.1.3 激活BeanFactoryPostProcessor

在了解BeanFactoryPostProcessor的用法后我们便可以深入的研究BeanFactoryPostProcessor的调用过程了,其是在方法invokeBeanFactoryPostProcessors(beanFactory)中实现的,进入到方法内部:

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

    // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
    // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
    if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
        beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
        beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }
}

public static void invokeBeanFactoryPostProcessors(
        ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {

    // Invoke BeanDefinitionRegistryPostProcessors first, if any.
    Set<String> processedBeans = new HashSet<>();

    if (beanFactory instanceof BeanDefinitionRegistry) {
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
        List<BeanFactoryPostProcessor> regularPostProcessors = new LinkedList<>();
        List<BeanDefinitionRegistryPostProcessor> registryProcessors = new LinkedList<>();

        for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
            if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
                BeanDefinitionRegistryPostProcessor registryProcessor =
                        (BeanDefinitionRegistryPostProcessor) postProcessor;
                registryProcessor.postProcessBeanDefinitionRegistry(registry);
                registryProcessors.add(registryProcessor);
            }
            else {
                regularPostProcessors.add(postProcessor);
            }
        }

        // Do not initialize FactoryBeans here: We need to leave all regular beans
        // uninitialized to let the bean factory post-processors apply to them!
        // Separate between BeanDefinitionRegistryPostProcessors that implement
        // PriorityOrdered, Ordered, and the rest.
        List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

        // First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
        String[] postProcessorNames =
                beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
        for (String ppName : postProcessorNames) {
            if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                processedBeans.add(ppName);
            }
        }
        sortPostProcessors(currentRegistryProcessors, beanFactory);
        registryProcessors.addAll(currentRegistryProcessors);
        invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
        currentRegistryProcessors.clear();

        // Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
        postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
        for (String ppName : postProcessorNames) {
            if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
                currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                processedBeans.add(ppName);
            }
        }
        sortPostProcessors(currentRegistryProcessors, beanFactory);
        registryProcessors.addAll(currentRegistryProcessors);
        invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
        currentRegistryProcessors.clear();

        // Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
        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);
            currentRegistryProcessors.clear();
        }

        // Now, invoke the postProcessBeanFactory callback of all processors handled so far.
        invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
        invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
    }

    else {
        // Invoke factory processors registered with the context instance.
        invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
    }

    // Do not initialize FactoryBeans here: We need to leave all regular beans
    // uninitialized to let the bean factory post-processors apply to them!
    String[] postProcessorNames =
            beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);

    // Separate between BeanFactoryPostProcessors that implement PriorityOrdered,
    // Ordered, and the rest.
    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)) {
            priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
        }
        else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
            orderedPostProcessorNames.add(ppName);
        }
        else {
            nonOrderedPostProcessorNames.add(ppName);
        }
    }

    // First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
    sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
    invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);

    // Next, invoke the BeanFactoryPostProcessors that implement Ordered.
    List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>();
    for (String postProcessorName : orderedPostProcessorNames) {
        orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
    }
    sortPostProcessors(orderedPostProcessors, beanFactory);
    invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);

    // Finally, invoke all other BeanFactoryPostProcessors.
    List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
    for (String postProcessorName : nonOrderedPostProcessorNames) {
        nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
    }
    invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);

    // Clear cached merged bean definitions since the post-processors might have
    // modified the original metadata, e.g. replacing placeholders in values...
    beanFactory.clearMetadataCache();
}

从上面的方法中我们可以看到对于BeanFactoryPostProcessor的处理主要分两种情况进行,一是对BeanDefinitionRegistry类的特殊处理,另一种是对普通BeanFactoryPostProcessor进行处理。而对于每种情况都需要考虑硬编码注入注册的后处理器已经通过配置注入的后处理器。
对于BeanDefinitionRegistry类型的处理类的处理主要包括以下内容:
(1)对于硬编码注册的后处理器的处理,主要通过AbstractApplicationContext中的添加处理器的方法addBeanFactoryPostProcessor进行添加如下:

    public void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor) {
        Assert.notNull(postProcessor, "BeanFactoryPostProcessor must not be null");
        this.beanFactoryPostProcessors.add(postProcessor);
    }

添加的后处理器会存在在beanFactoryPostProcessors中,而在处理BeanFactoryPostProcessor的时候会首先检查beanFactoryPostProcessors中是否有数据,当然BeanDefinitionRegistryPostProcessor继承自BeanFactoryPostProcessor,不但有BeanFactoryPostProcessor的特性,同时还有自己定义的个性化方法,也需要在此调用。所以这里需要从BeanFactoryPostProcessor中挑出BeanDefinitionRegistryPostProcessor的后处理器,并进行其postProcessBeanDefinitionRegistry方法的激活。
(2)记录后处理器主要使用了三个List完成。
- registryProcessors:记录通过硬编码方式注册的BeanDefinitionRegistryPostProcesser类型的后处理器
- regularPostProcessors:记录通过硬编码方式注册的BeanFactoryPostProcessor类型的后处理器
- registryPostProcessorsBeans:记录通过配置方式注册的BeanDefinitionRegistryPostProcesser类型的后处理器
(3)对以上记录的List中的后处理器统一调用BeanFactoryPostProcessor的postProcessBeanFactory方法。
(4)对BeanFactoryPostProcessors中非BeanDefinitionRegistryPostProcessor类型的后处理器进行统一的BeanFactoryPostProcessor的postProcessBeanFactory方法的调用。
(5)普通beanFactory处理
BeanDefinitionRegistryPostProcessor只对BeanDefinitionRegistry类型的ConfigurableListableBeanFactory有效,如果判断所示的BeanFactory并不是BeanDefinitionRegistry,那么便可以忽略BeanDefinitionRegistryPostProcessor,而直接处理BeanFactoryPostProcessor,当然获取的方式与上面的获取类似。

6.2 注册BeanPostProcessor

在上文中提到了BeanFactoryPostProcessor的调用,接下来我们就探索下BeanPostProcessor。但这里并不是调用,而是注册,真正的调用其实是在bean的实例化阶段进行的,这是一个很重要的步骤,也是很多功能BeanFactory不知道的重要原因。spring中大部分功能都是通过后处理器的方式进行扩展的,这是spring框架的一个特写,但是在BeanFactory中其实并没有实现后处理器的自动注册,所以在调用的时候如果没有进行手动注册其实是不能使用的。但是ApplicationContext中却添加了自动注册功能,如自定义一个后处理器:

public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        System.out.println("befor");
        return null;
    }
}

然后在配置文件中添加bean的配置:

<bean class="com.yhl.myspring.demo.applicationcontext.MyInstantiationAwareBeanPostProcessor"/>

这样的话再使用BeanFactory的方式进行加载的bean在加载时不会有任何改变的,而在使用ApplicationContext方式获取的bean时就会打印出“before”,而这个特性就是咋registryBeanPostProcessor方法中完成的。
我们继续深入分析registryBeanPostProcessors的方法实现:

protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}
public static void registerBeanPostProcessors(
        ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {

    String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);

    /* 
     * BeanPostProcessorChecker是一个普通的信息打印,可能会有些情况当spring的配置中的后
     * 处理器还没有被注册就已经开了bean的初始化,这时就会打印出BeanPostProcessorChecker中
     * 设定的信息
     */
    int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
    beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));

    //使用PriorityOrdered来保证顺序
    List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
    List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
    //使用Ordered来保证顺序
    List<String> orderedPostProcessorNames = new ArrayList<>();
    //无序BeanPostProcessor
    List<String> nonOrderedPostProcessorNames = new ArrayList<>();
    for (String ppName : postProcessorNames) {
        if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
            BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
            priorityOrderedPostProcessors.add(pp);
            if (pp instanceof MergedBeanDefinitionPostProcessor) {
                internalPostProcessors.add(pp);
            }
        }
        else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
            orderedPostProcessorNames.add(ppName);
        }
        else {
            nonOrderedPostProcessorNames.add(ppName);
        }
    }

    //第一步,注册所有实现了PriorityOrdered的BeanPostProcessor
    sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
    registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);

    //注册实现了Ordered的BeanPostProcessor
    List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>();
    for (String ppName : orderedPostProcessorNames) {
        BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
        orderedPostProcessors.add(pp);
        if (pp instanceof MergedBeanDefinitionPostProcessor) {
            internalPostProcessors.add(pp);
        }
    }
    sortPostProcessors(orderedPostProcessors, beanFactory);
    registerBeanPostProcessors(beanFactory, orderedPostProcessors);

    //注册所有的无序的BeanPostProcessor
    List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
    for (String ppName : nonOrderedPostProcessorNames) {
        BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
        nonOrderedPostProcessors.add(pp);
        if (pp instanceof MergedBeanDefinitionPostProcessor) {
            internalPostProcessors.add(pp);
        }
    }
    registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);

    //注册所有的内部BeanFactoryProcessor
    sortPostProcessors(internalPostProcessors, beanFactory);
    registerBeanPostProcessors(beanFactory, internalPostProcessors);

    // Re-register post-processor for detecting inner beans as ApplicationListeners,
    //添加ApplicationListener探测器
    beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
}

6.3 初始化消息资源

在进行这个函数解析前我们先回顾下spring国际化的使用方法。

6.3.1 spring国际化使用方法

假设我们正在开发一个支持多国语言的web应用,要求系统能够根据客户端的系统语言类型返回对应的界面:英文的操作系统返回英文界面,而中文的操作系统返回中文界面—–这便是典型的i18n国际化问题。对于有国际化要求的系统,我们不能简单的采用硬编码的方式写用户界面信息、保存信息等内容,而必须为这些需要国际的信息进行特殊处理。简单来说,就是为每种语言提供一套相应的资源文件,并以规范化命名的方式保存在特点的目录中,由系统自动根据客户端语言来选择合适的资源文件。
“国际化信息”也称为“本地化信息”,一般需要两个条件才可以确定一个特点类型的本地化信息,他们分别是“语言类型”和“国家/地区的类型”。如中文本地化信息既有中国大陆地区的中文,又有中国台湾地区、中国香港地区的中文,还有新加坡地区的中文。Java通过java.util.Locale类表示一个本地化对象,它允许通过语言参数和国家/地区参数创建一个确定的本地化对象。
java.util.Locale是表示语言和国家/地区信息的本地化类,它是创建国际化应用的基础,接下来我们就给出几个创建本地化对象的实例,如下代码:

        //1带有语言和国家/地区信息的本地化对象
        Locale locale1 = new Locale("zh","CN");
        //2只有语言信息的本地化对象
        Locale locale2 = new Locale("zh");
        //等同于1,Locale("zh","CN")
        Locale locale3 = Locale.CHINA;
        //等同于2,Locale("zh")
        Locale locale4 = Locale.CHINESE;
        //获取系统默认的本地化对象
        Locale locale5 = Locale.getDefault();

JDK的java.util包中提供了几个支持本地化的格式化操作工具类:NumberFormat、DateFormat、MessageFormat,而在spring中的国际化资源操作也无非是对于这几个类的封装操作,我们就以MessageFormat的用法来简单的回顾下,如下代码:

        String pattern1 ="{0},你好,欢迎你在{1}阅读{1}页的内容。";
        String pattern2 ="At {1,time,short} On {1,date,long},{0} paid {2,number,currency}";
        Object[] params = {"Xiaomin",new GregorianCalendar().getTime(),14};
        String message = MessageFormat.format(pattern1,params);
        MessageFormat messageFormat = new MessageFormat(pattern2,Locale.US);
        String message1 = messageFormat.format(params);

        System.out.println(message);
        System.out.println(message1);


执行结果如下:
Xiaomin,你好,欢迎你在18-4-20 上午9:35阅读18-4-20 上午9:35页的内容。
At 9:35 AM On April 20, 2018,Xiaomin paid $14.00

spring定义了访问国际化信息的MessageSource接口,并提供了几个易用的实现类。MessageSource分别被HierarchicalMessageSource和ApplicationContext接口扩展,这里我们主要看下HierarchicalMessageSource接口的几个实现类,如下图所示:
这里写图片描述
HierarchicalMessageSource接口最重要的两个实现类是ResourceBundleMessageSource和ReloadableResourceBundleMessageSource。它是基于java的ResourceBundle基础类实现的,允许通过资源名加载国际化资源。ReloadableResourceBundleMessageSource提供了定时刷新功能,允许在不重启系统的情况下更新资源信息。StaticMessageSource主要用于程序测试,他允许通过编程的方式提供国际化信息,而DelegatingMessageSource是为方便操作父MessageSource而提供的代理类,仅仅举例ResourceBundleMessageSource的实现方式。
(1)定义资源文件
message.properties(默认:英文),内容为:test=test
message_zh_CN.properties(简体中文):test=测试
然后打开命令行窗口,输入native2ascii -encoding gbk 文件所在目录\message_zh_CN.properties 文件所在目录\message_zh_CN_tem.properties,并将文件所在目录\message_zh_CN_tem.properties中的内容替换到文件所在目录\message_zh_CN.properties中,这样message_zh_CN.properties文件就存放的是转码后的内容了,比较简单。
(2)定义配置文件
在配置文件中添加一下bean的定义:

    <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="basenames">
            <list>
                <value>message</value>//对应资源文件的名称
            </list>
        </property>
    </bean>

其中这个bean的id必须命名为messageSource,否则会抛出NoSuchMessageException异常。
(3)通过ApplicationContext访问国际化资源

        String[] configs = {"applicationContext.xml"};
        ApplicationContext ctx = new ClassPathXmlApplicationContext(configs);
        Object[] params = {"Xiaomin",new GregorianCalendar().getTime()};

        String msg1 = ctx.getMessage("test",params,Locale.US);
        String msg2 = ctx.getMessage("test",params,Locale.CHINA);

        System.out.println(msg1);
        System.out.println(msg2);

6.3.2 初始化消息资源源码分析

上面我们在了解了spring的国际化的使用后接下来我们就进行源码分析,我们进入到initMessageSource方法中,如下:

protected void initMessageSource() {
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
        this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
        // Make MessageSource aware of parent MessageSource.
        if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
            HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
            if (hms.getParentMessageSource() == null) {
                // Only set parent context as parent MessageSource if no parent MessageSource
                // registered already.
                hms.setParentMessageSource(getInternalParentMessageSource());
            }
        }
        if (logger.isDebugEnabled()) {
            logger.debug("Using MessageSource [" + this.messageSource + "]");
        }
    }
    else {
        // Use empty MessageSource to be able to accept getMessage calls.
        DelegatingMessageSource dms = new DelegatingMessageSource();
        dms.setParentMessageSource(getInternalParentMessageSource());
        this.messageSource = dms;
        beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
        if (logger.isDebugEnabled()) {
            logger.debug("Unable to locate MessageSource with name '" + MESSAGE_SOURCE_BEAN_NAME +
                    "': using default [" + this.messageSource + "]");
        }
    }
}

我们看到方法的主要功能就是提取messageSource,并将其记录在spring的容器中,也就是AbstractApplicationContext中。当然,如果用户未设置资源文件的话,spring中也提供了默认的配置DelegatingMessageSource。
继续分析方法代码,其中获取自定义资源文件的方式:beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class),这里使用了硬编码的方式硬性规定了自定义资源文件必须为message,否则便会获取不到自定义资源配置,这也是为什么之前提到了Bean的id必须定义为messageSource。
继续跟踪代码,通过读取并将自定义资源文件配置记录在容器中,那么就可以在获取资源文件的时候直接使用了,例如,在AbstractApplicationContext中的获取资源文件属性的方法:

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

这其中的getMessage(code, args, locale)就是获取之前定义的资源文件。

6.4 初始化ApplicationEventMulticaster

初始化ApplicationEventMulticaster是在方法initApplicationEventMulticaster()中实现的,进入到方法体,如下:

protected void initApplicationEventMulticaster() {
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
            this.applicationEventMulticaster =
                    beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
            if (logger.isDebugEnabled()) {
                logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
            }
        }
        else {
            this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
            beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
            if (logger.isDebugEnabled()) {
                logger.debug("Unable to locate ApplicationEventMulticaster with name '" +
                        APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
                        "': using default [" + this.applicationEventMulticaster + "]");
            }
        }
    }

通过源码可以看到其实现逻辑与initMessageSource基本相同,其步骤如下:
(1)查找是否有name为applicationEventMulticaster的bean,如果有放到容器里,如果没有,初始化一个系统默认的SimpleApplicationEventMulticaster放入容器
(2)查找手动设置的applicationListeners,添加到applicationEventMulticaster里
(3)查找定义的类型为ApplicationListener的bean,设置到applicationEventMulticaster
(4)初始化完成、对earlyApplicationEvents里的事件进行通知(此容器仅仅是广播器未建立的时候保存通知信息,一旦容器建立完成,以后均直接通知)
(5)在系统操作时候,遇到的各种bean的通知事件进行通知
可以看到的是applicationEventMulticaster是一个标准的观察者模式,对于他内部的监听者applicationListeners,每次事件到来都会一一获取通知。

6.5 注册监听器

我们直接看源码,如下:

protected void registerListeners() {
        // Register statically specified listeners first.
        for (ApplicationListener<?> listener : getApplicationListeners()) {
            getApplicationEventMulticaster().addApplicationListener(listener);
        }

        // Do not initialize FactoryBeans here: We need to leave all regular beans
        // uninitialized to let post-processors apply to them!
        String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
        for (String listenerBeanName : listenerBeanNames) {
            getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
        }

        // Publish early application events now that we finally have a multicaster...
        Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
        this.earlyApplicationEvents = null;
        if (earlyEventsToProcess != null) {
            for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
                getApplicationEventMulticaster().multicastEvent(earlyEvent);
            }
        }
    }
CloudCompare横截面工具实战:从点云切片到轮廓提取的完整流程 本文详细介绍了CloudCompare软件中Cross Section工具的核心功能与实战流程。从交互式裁剪框的精准操控,到包络线提取的参数设置与优化,再到内部轮廓提取及批量处理技巧,完整解析了如何从三维点云中高效提取建筑立面、隧道断面等关键轮廓信息,是点云数据处理与分析的实用指南。 阅读详情

相关推荐

量化人必看|miniQMT踩坑记:回测太慢?一招教你把行情数据“搬回家”!

这篇文章主要聊的是。

奔跑的蜗牛 3479

Spring源码解析(8)——ApplicationContext

回顾上篇 上篇文章《属性注入》,我们已经分析完了bean中的属性是如何注入进去的。到目前为止我们已经分析完了Spring创建一个bean的全流程。先是解析配置的xml文件,将xml文件里配置的信息解析到一个BeanDefinition里面,然后通过BeanDefinition来创建bean,同时也会将bean的属性值注入。这里面也涉及到一个循环依赖的问题,也分析了一下Spring是如何来解决循环依赖的。上面的分析我们都是基于BeanFacotry接口的实现类XmlBeanFactory来分析的,在Sprin

tack_back的博客 164

STM32+ESP8266接入OneNet物联网平台全流程

MQTT协议是物联网设备与云平台通信的主流轻量级消息传输标准,其发布/订阅模型天然适配嵌入式资源受限场景;OneJSON作为中国移动OneNet平台定制的数据格式,在标准JSON基础上精简Schema、降低解析开销,显著提升STM32等MCU的处理效率。该技术组合兼顾安全性、低功耗与工程可量产性,广泛应用于环境监测、智能仪表、工业传感器等远程数据采集系统。实际落地需协同完成云平台物模型定义、Token动态认证、MQTT Topic严格构造、AT指令可靠交互及单总线传感器数据融合等关键环节。本文以DS18B2

u5v6w7的博客 1120

spring boot实战(第十篇)Spring boot Bean加载源码分析

前言 前面的文章描述了Application对应Bean的创建,本篇将阐述spring boot中bean的创建过程 refresh 首先来看SpringApplication#run方法中refresh()方法 // Refresh the context refresh(context); 调用的是AbstractApplicationContext中的re

liaokailin的专栏 4万+

SigFit实战:从FEA到光学仿真的热力耦合分析全流程解析

在光机系统与芯片封装等精密工程领域,热与力的耦合效应是影响产品性能与可靠性的核心物理问题。有限元分析(FEA)是预测结构在热、力载荷下变形与应力的基础工具,但其结果通常无法直接用于评估光学性能。这需要一座桥梁,将物理场数据转化为光学设计语言。其核心原理在于数据映射与面形表征技术,例如通过泽尼克多项式拟合,将离散的节点位移分解为离焦、像散等标准光学像差。这项技术的工程价值在于,它实现了多物理场仿真的闭环,使工程师能在设计阶段精准预测热变形或应力对波前误差、调制传递函数等关键光学指标的影响。其典型应用场景包括高

weixin_28630185的博客 342

ArcFace vs 传统算法:实测MTCNN+ArcFace在PyCharm中的识别准确率(含完整对比数据)

本文通过PyCharm开发环境实测MTCNN+ArcFace组合在人脸识别中的性能表现,并与传统算法进行多维度对比。实验数据显示,ArcFace在LFW数据集上达到99.83%的准确率,显著优于传统方法。文章还提供了完整的代码实现和工程优化建议,为开发者提供实用的技术参考。

醉三国 235

Spring源码阅读——PostProcessor(一)

PostProcessor系列接口 一共有3种PostProcessor接口,分别是: BeanDefinitionRegistryPostProcessor BeanFactoryPostProcessor BeanPostProcessor 它们都是在容器进行refrush的时候,在不同阶段进行调用,其相对的调用顺序是: 1->2->3 提供的这三种PostProcessor...

困兽的博客 361

六自由度机器人(机械臂)运动学建模及运动规划系列(一)——简介

毕业设计做了六轴机器人相关的课题,做完之后学到很多,在这里分享一下。

weixin_47849087的博客 2万+

Spring源码深度解析总结(7)—— 容器的扩展功能

经过之前的分析,我们已经了解了Spring是如何解析和加载的bean的。但是在上一篇文章结束的时候说到,一般我们写程序是不会用到BeanFactory来当做Spring的容器的,一般使用的是ApplicationContext作为Spring的容器。这两者之间有什么区别呢?ApplicationContext和BeanFactory在Spring中都是用于加载bean的,但是Application...

vergil小童鞋的博客 1123

西门子G120变频器在立体仓库堆垛机应用参数设置

本文详细介绍了G120 CU240E及CU250S变频器的参数设置流程。主要内容包括:1)快速调试步骤,从新建项目、扫描节点到设置电机类型、负载类型等参数;2)抱闸参数设置方法;3)参数下载异常时的处理方案。重点说明了通过STARTER软件进行向导式调试的完整过程,包括电机铭牌参数输入、识别方式选择等关键环节,并提示了通讯连接注意事项。当下载失败时,提供了通过导入导出项目文件解决问题的具体方法。

xdpcxq的专栏 864

使用手机录制rosbag包

使用手机作为便携式数据采集设备来录制rosbag。

weixin_38582851的博客 616

Spring源码深度解析 容器的功能扩展总结

ApplicationContext 和 BeanFacotry 两者都是用于加载 Bean 的,但是相比之下, ApplicationContext 提供了更多的扩展功能,简单一点说 ApplicationContext 包含 BeanFactory 的所有功能 通常建议比 BeanFactory 优先 ,除非在一些限制的场合,比如字节长度对内存有很大的影响时 (Applet) 使用 BeanFactory 方式加载 XML 使用 ApplicationContext 方式加载 XML ClassP

勤劳的搬运工 965

在 FlexSim 中建 AGV 小车模型时,如果设定“每次充电需要 2 小时才能充满”,应该如何配置 AGV 的充电功能?

🏆本文收录于 《全栈 Bug 调优(实战版)》 专栏。专栏聚焦真实项目中的各类疑难 Bug,从成因剖析 → 排查路径 → 解决方案 → 预防优化全链路拆解,形成一套可复用、可沉淀的实战知识体系。无论你是初入职场的开发者,还是负责复杂项目的资深工程师,都可以在这里构建一套属于自己的「问题诊断与性能调优」方法论,助你稳步进阶、放大技术价值。

**My Coding Family** 882

Spring源码深度解析-第6章《容器的功能扩展》总结

1、ClassPathXMLApplicationContext初始化的步骤 1)初始化前的准备工作,例如对系统属性或者环境变量进行准备及验证; 2)初始化BeanFactory,并进行XML文件读取; 3)对beanFactory进行各种功能填充; 4)子类覆盖方法做额外的处理; 5)激活各种BeanFactory处理器; 6)注册拦截bean创建的bean处理器; 7)为上下文初...

QQsilhonette的博客 211

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Spring5.0源码深度解析之容器的功能扩展

版权所有:https://my.oschina.net/u/3995125,禁止转载 容器的功能扩展 前期准备: 同样,我们还是以AnnotationConfigApplicationContext 作为切入点,开始对整体功能进行分析。 AnnotationCon...

chuanyingcao2675的博客 237

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

Spring源码深度解析笔记(13)——容器的功能扩展

经过前几章的分析,对Spring中的容器功能有了简单的了解,在前面几章中一直以BeanFactory接口以及它的默认实现类XmlBeanFacotory为例进行分析,但是Spring中还提供了另一个接口ApplicationContext,用于扩展BeanFactory中现有的功能。 ApplicationContext和BeanFactory两者都是用于加载Bean的,但是相比之下,Applic...

wuyingya_12的博客 463

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

《Spring源码深度解析》学习笔记-容器的功能扩展

ApplicationContext接口用于扩展BeanFactory中现有的功能,二者都用于加载bean, ApplicationContext包含BeanFactory所有的功能,通常建议比BeanFactory优先使用。 二者写法上的差异: BeanFactory加载XML: BeanFactory bf=new XmlBeanFactory...

312

spring容器的功能扩展

容器的扩展功能主要实现为: org.springframework.context.support.AbstractApplicationContext.refresh() public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupS...

weixin_30664539的博客 113

spring源码深度解析

《Spring源码深度解析》_ByteX的博客-优快云博客

lmy6688的博客 222

spring基础夯实(二)

创建A时会首先记录A对应的beanName,并将创建工厂加入缓存中,对A的属性填充yeshidi奥永populate方法的时候对B进行递归创建,因为B也存在A属性,因此再实例化B的populate方法中又会再次初始化B,直接调用ObjectFactory去创建A。在B中创建依赖A时通过ObjectFactory提供的实例化方法来中断A中的属性填充,使B中持有的A是刚刚初始化没有任何属性的A,因为A与B中的A所表示的属性地址是一样的,所以在A创建好的属性填充自然可以通过B中的A获取,这样就解决了循环依赖。

weixin_43643315的博客 648

Spring源码解析——容器的功能扩展

ApplicationContext和BeanFactory都是用于加载bean的,但是相比之下,ApplicationContext提供了更多的扩展功能,ApplicationContext包含BeanFactory的所有功能,通常建议比BeanFactory优先,除非在一些限制的场合,比如字节长度对内存有很大的影响时。设置了路径是必不可少的步骤,ClassPathXmlApplicationContext中可以将配置文件路径以数组的方式传入,可以对数组进行解析并加载。

qq_16500963的博客 151

spring源码分析 容器的功能拓展

注意:本文摘自 spring源码深度解析 简介 经过前面几章的分析,相信大家已经对Spring中的容器功能有了简单的了解,在前面的章 节中我们一直以BeanFactory接口以及它的默认实现类XmlBeanFactory为例进行分析,但是, Spring中还提供了另一个接口ApplicationContext, 用于扩展BeanFactory中现有的功能。 ApplicationContext和BeanFactory两者都是用于加载Bean的,但是相比之下,Application­Context提供.

今天吃了吗的博客 391

Swift Package Manager `swift package unedit` 命令完全指南:退出可编辑模式与依赖恢复机制

`swift package unedit` 是 Swift Package Manager(SwiftPM)中与 `swift package edit` 配对使用的核心命令,用于将某个已进入"可编辑模式"(editable mode)的远程依赖恢复为正常的受管状态。本文基于当前仓库的官方命令文档([Sources/PackageManagerDocs/Documentation.docc/Pa

gitblog_00526的博客 155

《Spring源码深度解析》

Spring整体架构和环境搭建 Spring整体架构 Spring Core:框架的最基础部分,提供 IoC 容器,对 bean 进行管理。 Spring Context:继承BeanFactory,提供上下文信息,扩展出JNDI、EJB、电子邮件、国际化等功能。 Spring DAO:提供了JDBC的抽象层,还提供了声明性事务管理方法。 Spring ORM:提供了JPA、JDO、Hibernate、MyBatis 等ORM映射层. Spring AOP:集成了所有AOP功能 Spring Web:提供

ByteX的博客 4万+

Spring深度学习:IOC容器及源码解析

大家经常戏称Java工程师为Spring工程师,毫无疑问,这句话体现出Spring框架在Java开发中的重要性和普及度。本文小豪将带大家深度学习Spring IOC底层源码,学习过程中不仅仅是了解Spring IOC的工作原理,更重要的是学会理解框架的底层逻辑,从而提升我们的思维能力。

Code豪客 1677

Spring容器启动的隐藏关卡:onRefresh()扩展点深度解析

Spring容器启动过程中的onRefresh()方法是一个关键但易被忽视的扩展点,位于核心基础设施初始化后、单例Bean实例化前,为子类容器(如Web或响应式应用)提供专属初始化入口。

张小洛的BOLG 1130
上一篇: spring源码深度解析---容器的功能扩展(上)
下一篇: spring源码深度解析---AOP:动态AOP使用
小潭渔
博客等级 码龄18年 190粉丝 122原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值