MessageSourceAware源碼分析

MessageSourceAware

一庞瘸、基本信息

?? 作者 - Lex ?? 博客 - 我的CSDN ?? 文章目錄 - 所有文章 ?? 源碼地址 - MessageSourceAware源碼

二、接口描述

MessageSourceAware 接口赠叼,主要用于對象希望被注入MessageSource擦囊。MessageSource是Spring中用于國際化(i18n)的接口,它提供了從不同的消息資源(例如:屬性文件)獲取消息的方法嘴办。使用MessageSource瞬场,我們可以為應(yīng)用程序提供國際化的消息支持。

三涧郊、接口源碼

MessageSourceAware 是 Spring 框架自 1.1.1 開始引入的一個(gè)核心接口贯被。實(shí)現(xiàn)MessageSourceAware接口的對象會在Spring容器中被自動注入一個(gè)MessageSource實(shí)例。

/**
 * 任何希望被通知運(yùn)行其中的MessageSource(通常是ApplicationContext)的對象需要實(shí)現(xiàn)的接口。
 *
 * 注意彤灶,MessageSource通常也可以作為bean引用傳遞
 * (到任意的bean屬性或構(gòu)造函數(shù)參數(shù))看幼,因?yàn)樗趹?yīng)用上下文中通常是以"name"為messageSource的bean定義的。
 * 
 * 作者: Juergen Hoeller, Chris Beams
 * 版本: 1.1.1
 * 參見: ApplicationContextAware
 */
public interface MessageSourceAware extends Aware {

    /**
     * 設(shè)置此對象運(yùn)行的MessageSource幌陕。
     * 此方法在常規(guī)bean屬性被填充之后調(diào)用诵姜,但在初始化回調(diào)(如InitializingBean的afterPropertiesSet或自定義的init-method)之前調(diào)用。
     * 此方法在ApplicationContextAware的setApplicationContext方法之前被調(diào)用搏熄。
     * 
     * @param messageSource 此對象要使用的消息源
     */
    void setMessageSource(MessageSource messageSource);

}

四棚唆、主要功能

  1. 自動注入

    • 當(dāng)一個(gè)bean實(shí)現(xiàn)了MessageSourceAware接口,并且被Spring容器管理時(shí)搬卒,Spring將會自動調(diào)用該bean的setMessageSource方法瑟俭,傳入當(dāng)前應(yīng)用上下文的MessageSource實(shí)例。
  2. 國際化支持

    • 通過MessageSourceAware契邀,beans可以獲得對MessageSource的訪問權(quán)摆寄,從而可以根據(jù)不同的地區(qū)和語言獲取相應(yīng)的消息。這對于需要顯示不同語言的錯(cuò)誤消息坯门、UI標(biāo)簽或其他用戶面向的文本的beans特別有用微饥。
  3. 簡化配置

    • 雖然我們可以通過常規(guī)的依賴注入方法將MessageSource注入到beans中,但MessageSourceAware提供了一種更加自動化和明確的方法古戴,特別是當(dāng)我們的bean需要在初始化過程的特定階段獲得MessageSource時(shí)欠橘。

五、最佳實(shí)踐

首先來看看啟動類入口现恼,上下文環(huán)境使用AnnotationConfigApplicationContext(此類是使用Java注解來配置Spring容器的方式)肃续,構(gòu)造參數(shù)我們給定了一個(gè)MyConfiguration組件類。然后從Spring上下文中獲取一個(gè)MyMessageSourceAware類型的bean叉袍,最后調(diào)用getMessage方法始锚。

public class MessageSourceAwareApplication {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
        MyMessageSourceAware messageSourceAware = context.getBean(MyMessageSourceAware.class);
        messageSourceAware.getMessage();
    }
}

這里使用@Bean注解,定義了兩個(gè)Bean喳逛,是為了確保 MyMessageSourceAware 瞧捌,MessageSource被 Spring 容器執(zhí)行。其中ResourceBundleMessageSource 是 Spring 框架中用于國際化(i18n)的一個(gè)具體實(shí)現(xiàn)润文。它為應(yīng)用程序提供了從屬性文件中讀取國際化消息的能力姐呐。

@Configuration
public class MyConfiguration {

    @Bean
    public MyMessageSourceAware myMessageSourceAware(){
        return new MyMessageSourceAware();
    }

    @Bean
    public MessageSource messageSource() {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        messageSource.setBasename("i18n/messages");
        return messageSource;
    }
}

MyMessageSourceAware類使用MessageSourceAware接口來自動獲得對MessageSource的引用。這個(gè)引用可以用來根據(jù)不同的語言或地區(qū)檢索國際化的消息典蝌。然后利用注入的MessageSource曙砂,從屬性文件中檢索并打印兩個(gè)國際化的消息,一個(gè)是英文的骏掀,另一個(gè)是簡體中文的鸠澈。

public class MyMessageSourceAware implements MessageSourceAware {

    private MessageSource messageSource;

    @Override
    public void setMessageSource(MessageSource messageSource) {
        this.messageSource = messageSource;
    }

    public void getMessage() {
        System.out.println("English:"+messageSource.getMessage("greeting", null, Locale.ENGLISH));
        System.out.println("中文:"+messageSource.getMessage("greeting", null, Locale.SIMPLIFIED_CHINESE));
    }
}

運(yùn)行結(jié)果發(fā)現(xiàn)乔夯,MyMessageSourceAware類已成功從屬性文件中獲取了國際化消息。

English:Hello!
中文:我們好

六款侵、時(shí)序圖

sequenceDiagram
    Title: EnvironmentAware時(shí)序圖
    participant MessageSourceAwareApplication
    participant AnnotationConfigApplicationContext
    participant AbstractApplicationContext
    participant DefaultListableBeanFactory
    participant AbstractBeanFactory
    participant DefaultSingletonBeanRegistry
    participant AbstractAutowireCapableBeanFactory
    participant ApplicationContextAwareProcessor
    participant MyMessageSourceAware
    
    MessageSourceAwareApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)<br>創(chuàng)建上下文
    AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()<br>刷新上下文
    AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)<br>初始化Bean工廠
    AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()<br>實(shí)例化單例
    DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name)<br>獲取Bean
    AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly)<br>執(zhí)行獲取Bean
    AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory)<br>獲取單例Bean
    DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject()<br>獲取Bean實(shí)例
    AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args)<br>創(chuàng)建Bean
    AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args)<br>執(zhí)行Bean創(chuàng)建
    AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName,bean,mbd)<br>負(fù)責(zé)bean的初始化
    AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyBeanPostProcessorsBeforeInitialization(existingBean, beanName)<br>調(diào)用前置處理器
    AbstractAutowireCapableBeanFactory->>ApplicationContextAwareProcessor:postProcessBeforeInitialization(bean,beanName)<br>觸發(fā)Aware處理
    ApplicationContextAwareProcessor->>ApplicationContextAwareProcessor:invokeAwareInterfaces(bean)<br>執(zhí)行Aware回調(diào)
    ApplicationContextAwareProcessor->>MyMessageSourceAware:setMessageSource(messageSource)<br>設(shè)置messageSource
    AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean對象
    AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean對象
    AnnotationConfigApplicationContext-->>MessageSourceAwareApplication:初始化完成

七末荐、源碼分析

首先來看看啟動類入口,上下文環(huán)境使用AnnotationConfigApplicationContext(此類是使用Java注解來配置Spring容器的方式)新锈,構(gòu)造參數(shù)我們給定了一個(gè)MyConfiguration組件類缠借。然后從Spring上下文中獲取一個(gè)MyMessageSourceAware類型的bean亥至,最后調(diào)用getMessage方法桑李。

public class MessageSourceAwareApplication {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
        MyMessageSourceAware messageSourceAware = context.getBean(MyMessageSourceAware.class);
        messageSourceAware.getMessage();
    }
}

org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext構(gòu)造函數(shù)中宵统,執(zhí)行了三個(gè)步驟,我們重點(diǎn)關(guān)注refresh()方法

public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
    this();
    register(componentClasses);
    refresh();

org.springframework.context.support.AbstractApplicationContext#refresh方法中拳缠,我們重點(diǎn)關(guān)注一下finishBeanFactoryInitialization(beanFactory)這方法會對實(shí)例化所有剩余非懶加載的單列Bean對象墩新,其他方法不是本次源碼閱讀的重點(diǎn)暫時(shí)忽略。

@Override
public void refresh() throws BeansException, IllegalStateException {
    // ... [代碼部分省略以簡化]
    
    // 步驟1. Initialize message source for this context.
    initMessageSource();
    
    // 步驟2. Instantiate all remaining (non-lazy-init) singletons.
    finishBeanFactoryInitialization(beanFactory);
    // ... [代碼部分省略以簡化]
}

我們來到org.springframework.context.support.AbstractApplicationContext#refresh方法中的步驟1窟坐,在org.springframework.context.support.AbstractApplicationContext#initMessageSource方法中海渊,這個(gè)方法確保Spring應(yīng)用上下文總是有一個(gè)MessageSource bean可用,無論是否明確定義了它哲鸳。如果用戶沒有定義臣疑,它會提供一個(gè)默認(rèn)實(shí)現(xiàn)。這意味著在Spring上下文中徙菠,我們總是可以安全地調(diào)用getMessage()讯沈,因?yàn)榭倳幸粋€(gè)MessageSource可用。

protected void initMessageSource() {
    // 獲取Bean工廠
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    // 檢查是否已經(jīng)存在名為MESSAGE_SOURCE_BEAN_NAME的bean
    if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
        // 如果存在婿奔,則獲取該bean
        this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
        // 如果當(dāng)前MessageSource具有層次結(jié)構(gòu)并且沒有設(shè)置父MessageSource
        if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
            HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
            if (hms.getParentMessageSource() == null) {
                // 設(shè)置父上下文作為父MessageSource(如果之前沒有注冊過父MessageSource)
                hms.setParentMessageSource(getInternalParentMessageSource());
            }
        }
        // ... [代碼部分省略以簡化]
    }
    else {
        // 如果不存在MESSAGE_SOURCE_BEAN_NAME的bean缺狠,則創(chuàng)建一個(gè)DelegatingMessageSource并注冊到上下文
        // 使用DelegatingMessageSource以便能夠處理getMessage調(diào)用
        DelegatingMessageSource dms = new DelegatingMessageSource();
        dms.setParentMessageSource(getInternalParentMessageSource());
        this.messageSource = dms;
        beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
        // ... [代碼部分省略以簡化]
    }
}

我們來到org.springframework.context.support.AbstractApplicationContext#refresh方法中的步驟2,在org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization方法中萍摊,會繼續(xù)調(diào)用DefaultListableBeanFactory類中的preInstantiateSingletons方法來完成所有剩余非懶加載的單列Bean對象挤茄。

/**
 * 完成此工廠的bean初始化,實(shí)例化所有剩余的非延遲初始化單例bean记餐。
 * 
 * @param beanFactory 要初始化的bean工廠
 */
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
    // ... [代碼部分省略以簡化]
    // 完成所有剩余非懶加載的單列Bean對象驮樊。
    beanFactory.preInstantiateSingletons();
}

org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons方法中薇正,主要的核心目的是預(yù)先實(shí)例化所有非懶加載的單例bean片酝。在Spring的上下文初始化完成后,該方法會被觸發(fā)挖腰,以確保所有單例bean都被正確地創(chuàng)建并初始化雕沿。其中getBean(beanName)是此方法的核心操作。對于容器中定義的每一個(gè)單例bean猴仑,它都會調(diào)用getBean方法审轮,這將觸發(fā)bean的實(shí)例化肥哎、初始化及其依賴的注入。如果bean之前沒有被創(chuàng)建過疾渣,那么這個(gè)調(diào)用會導(dǎo)致其被實(shí)例化和初始化篡诽。

public void preInstantiateSingletons() throws BeansException {
    // ... [代碼部分省略以簡化]
    // 循環(huán)遍歷所有bean的名稱
    for (String beanName : beanNames) {
        getBean(beanName);
    }
    // ... [代碼部分省略以簡化]
}

org.springframework.beans.factory.support.AbstractBeanFactory#getBean()方法中,又調(diào)用了doGetBean方法來實(shí)際執(zhí)行創(chuàng)建Bean的過程榴捡,傳遞給它bean的名稱和一些其他默認(rèn)的參數(shù)值杈女。此處,doGetBean負(fù)責(zé)大部分工作吊圾,如查找bean定義达椰、創(chuàng)建bean(如果尚未創(chuàng)建)、處理依賴關(guān)系等项乒。

@Override
public Object getBean(String name) throws BeansException {
    return doGetBean(name, null, null, false);
}

org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean方法中啰劲,首先檢查所請求的bean是否是一個(gè)單例并且已經(jīng)創(chuàng)建。如果尚未創(chuàng)建檀何,它將創(chuàng)建一個(gè)新的實(shí)例蝇裤。在這個(gè)過程中,它處理可能的異常情況频鉴,如循環(huán)引用猖辫,并確保返回的bean是正確的類型。這是Spring容器bean生命周期管理的核心部分砚殿。

protected <T> T doGetBean(
        String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
        throws BeansException {
    // ... [代碼部分省略以簡化]

    // 開始創(chuàng)建bean實(shí)例
    if (mbd.isSingleton()) {
        // 如果bean是單例的啃憎,我們會嘗試從單例緩存中獲取它
        // 如果不存在,則使用lambda創(chuàng)建一個(gè)新的實(shí)例
        sharedInstance = getSingleton(beanName, () -> {
            try {
                // 嘗試創(chuàng)建bean實(shí)例
                return createBean(beanName, mbd, args);
            }
            catch (BeansException ex) {
                // ... [代碼部分省略以簡化]
            }
        });
        // 對于某些bean(例如FactoryBeans)似炎,可能需要進(jìn)一步處理以獲取真正的bean實(shí)例
        beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    }
    // ... [代碼部分省略以簡化]

    // 確保返回的bean實(shí)例與請求的類型匹配
    return adaptBeanInstance(name, beanInstance, requiredType);
}

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()方法中辛萍,主要負(fù)責(zé)從單例緩存中獲取一個(gè)已存在的bean實(shí)例,或者使用提供的ObjectFactory創(chuàng)建一個(gè)新的實(shí)例羡藐。這是確保bean在Spring容器中作為單例存在的關(guān)鍵部分贩毕。

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    // 斷言bean名稱不能為空
    Assert.notNull(beanName, "Bean name must not be null");

    // 同步訪問單例對象緩存,確保線程安全
    synchronized (this.singletonObjects) {
        // 從緩存中獲取單例對象
        Object singletonObject = this.singletonObjects.get(beanName);

        // 如果緩存中沒有找到
        if (singletonObject == null) {
            // ... [代碼部分省略以簡化]

            try {
                // 使用工廠創(chuàng)建新的單例實(shí)例
                singletonObject = singletonFactory.getObject();
                newSingleton = true;
            }
            catch (IllegalStateException ex) {
                // ... [代碼部分省略以簡化]
            }
            catch (BeanCreationException ex) {
                // ... [代碼部分省略以簡化]
            }
            finally {
                // ... [代碼部分省略以簡化]
            }

            // ... [代碼部分省略以簡化]
        }

        // 返回單例對象
        return singletonObject;
    }
}

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()方法中仆嗦,主要的邏輯是調(diào)用 doCreateBean辉阶,這是真正進(jìn)行 bean 實(shí)例化、屬性填充和初始化的地方瘩扼。這個(gè)方法會返回新創(chuàng)建的 bean 實(shí)例谆甜。

@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
    throws BeanCreationException {
    
    // ... [代碼部分省略以簡化]
    
    try {
        // 正常的bean實(shí)例化、屬性注入和初始化集绰。
        // 這里是真正進(jìn)行bean創(chuàng)建的部分规辱。
        Object beanInstance = doCreateBean(beanName, mbdToUse, args);
        // 記錄bean成功創(chuàng)建的日志
        if (logger.isTraceEnabled()) {
            logger.trace("Finished creating instance of bean '" + beanName + "'");
        }
        return beanInstance;
    }
    catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
        // ... [代碼部分省略以簡化]
    }
    catch (Throwable ex) {
        // ... [代碼部分省略以簡化]
    }
}

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean方法中,initializeBean方法是bean初始化栽燕,確保bean是完全配置和準(zhǔn)備好的罕袋。

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
        throws BeanCreationException {

    // 聲明一個(gè)對象改淑,后續(xù)可能用于存放初始化后的bean或它的代理對象
    Object exposedObject = bean;

    // ... [代碼部分省略以簡化]
    
    try {
        // ... [代碼部分省略以簡化]
        
        // bean初始化
        exposedObject = initializeBean(beanName, exposedObject, mbd);
    } 
    catch (Throwable ex) {
        // ... [代碼部分省略以簡化]
    }

    // 返回創(chuàng)建和初始化后的bean
    return exposedObject;
}

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean方法中,如果條件滿足(即 bean 不是合成的)浴讯,那么它會調(diào)用 applyBeanPostProcessorsBeforeInitialization 方法朵夏。這個(gè)方法是 Spring 生命周期中的一個(gè)關(guān)鍵點(diǎn),它會遍歷所有已注冊的 BeanPostProcessor 實(shí)現(xiàn)榆纽,并調(diào)用它們的 postProcessBeforeInitialization 方法侍郭。這允許我們和內(nèi)部處理器在 bean 初始化之前對其進(jìn)行修改或執(zhí)行其他操作。

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {

    // ... [代碼部分省略以簡化]
    
    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }
    
    // ... [代碼部分省略以簡化]

    return wrappedBean;
}

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization方法中掠河,遍歷每一個(gè) BeanPostProcessorpostProcessBeforeInitialization 方法都有機(jī)會對bean進(jìn)行修改或增強(qiáng)

@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
    throws BeansException {

    Object result = existingBean;
    for (BeanPostProcessor processor : getBeanPostProcessors()) {
        Object current = processor.postProcessBeforeInitialization(result, beanName);
        if (current == null) {
            return result;
        }
        result = current;
    }
    return result;
}

org.springframework.context.support.ApplicationContextAwareProcessor#postProcessBeforeInitialization方法中亮元,在這個(gè)方法的實(shí)現(xiàn)特別關(guān)注那些實(shí)現(xiàn)了 "aware" 接口的 beans,并為它們提供所需的運(yùn)行環(huán)境信息唠摹。

@Override
@Nullable
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    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);

    return bean;
}

org.springframework.context.support.ApplicationContextAwareProcessor#invokeAwareInterfaces方法中爆捞,用于處理實(shí)現(xiàn)了"Aware"接口的beans。這些接口使得beans能夠被自動"感知"并獲得對其運(yùn)行環(huán)境或特定依賴的引用勾拉,而不需要顯式地進(jìn)行查找或注入煮甥。

private void invokeAwareInterfaces(Object bean) {
    // ... [代碼部分省略以簡化]
    if (bean instanceof MessageSourceAware) {
        ((MessageSourceAware) bean).setMessageSource(this.applicationContext);
    }
    // ... [代碼部分省略以簡化]
}

最后執(zhí)行到我們自定義的邏輯中,MyMessageSourceAware類使用MessageSourceAware接口來自動獲得對MessageSource的引用藕赞。這個(gè)引用可以用來根據(jù)不同的語言或地區(qū)檢索國際化的消息成肘。然后利用注入的MessageSource,從屬性文件中檢索并打印兩個(gè)國際化的消息斧蜕,一個(gè)是英文的双霍,另一個(gè)是簡體中文的。

public class MyMessageSourceAware implements MessageSourceAware {

    private MessageSource messageSource;

    @Override
    public void setMessageSource(MessageSource messageSource) {
        this.messageSource = messageSource;
    }

    public void getMessage() {
        System.out.println("English:"+messageSource.getMessage("greeting", null, Locale.ENGLISH));
        System.out.println("中文:"+messageSource.getMessage("greeting", null, Locale.SIMPLIFIED_CHINESE));
    }
}

八批销、注意事項(xiàng)

  1. 明確的配置

    • 確保我們的Spring上下文中有一個(gè)MessageSource bean洒闸,通常命名為“messageSource”。雖然Spring提供了一個(gè)默認(rèn)的均芽,但為了滿足自定義需求丘逸,我們可能需要明確地配置它。
  2. 生命周期時(shí)機(jī)

    • MessageSourceAwaresetMessageSource方法在常規(guī)屬性設(shè)置之后和初始化方法(如InitializingBeanafterPropertiesSet或任何自定義的init方法)之前被調(diào)用掀宋。確保我們的bean不在其生命周期的早期階段(例如深纲,在構(gòu)造函數(shù)中)期望使用MessageSource
  3. 文件位置和命名

    • 如果我們使用ResourceBundleMessageSource或類似的機(jī)制劲妙,確保我們的屬性文件位于類路徑上湃鹊,并且與我們在MessageSource配置中指定的basename匹配。
  4. 編碼問題

    • 屬性文件默認(rèn)使用ISO-8859-1編碼是趴。如果我們的消息包含非此編碼的字符(例如中文涛舍、俄文等)澄惊,確保使用Unicode轉(zhuǎn)義或正確設(shè)置文件的編碼唆途。
  5. 父子上下文

    • 在使用Spring的父子上下文(例如富雅,在Web應(yīng)用中)時(shí),子上下文可以訪問父上下文中的MessageSource肛搬,但反之則不行没佑。確保我們在正確的上下文中配置了MessageSource
  6. 避免硬編碼

    • 盡量不要在代碼中硬編碼消息鍵或默認(rèn)消息温赔。最好在屬性文件中管理它們蛤奢,這樣在未來需要更改或添加新的語言支持時(shí),我們不需要修改代碼陶贼。
  7. 默認(rèn)消息

    • 當(dāng)使用MessageSource檢索消息時(shí)啤贩,考慮提供一個(gè)默認(rèn)消息。這可以在未找到特定消息時(shí)提供一個(gè)后備拜秧,避免拋出異常痹屹。

八、總結(jié)

最佳實(shí)踐總結(jié)

  1. 啟動類

    • MessageSourceAwareApplication類中枉氮,使用了AnnotationConfigApplicationContext來啟動Spring應(yīng)用志衍。這個(gè)上下文是專為基于Java注解的配置而設(shè)計(jì)的。啟動時(shí)聊替,它加載了MyConfiguration配置類楼肪,并從上下文中獲取了MyMessageSourceAwarebean,隨后調(diào)用了getMessage方法顯示消息惹悄。
  2. 配置類

    • MyConfiguration是一個(gè)基于Java的Spring配置類春叫,其中定義了兩個(gè)bean:MyMessageSourceAwaremessageSourcemessageSource bean是一個(gè)ResourceBundleMessageSource實(shí)例泣港,用于從i18n/messages基本名稱的屬性文件中讀取國際化消息象缀。
  3. 實(shí)現(xiàn)MessageSourceAware接口

    • MyMessageSourceAware類實(shí)現(xiàn)了MessageSourceAware接口,這意味著Spring容器會自動注入一個(gè)MessageSource實(shí)例到這個(gè)bean中爷速。這是通過setMessageSource方法完成的央星。
  4. 消息檢索

    • MyMessageSourceAwaregetMessage方法中,使用了注入的MessageSource來檢索和打印兩種語言的國際化消息:英文和簡體中文惫东。
  5. 運(yùn)行結(jié)果

    • 當(dāng)應(yīng)用程序執(zhí)行時(shí)莉给,它成功地從對應(yīng)的屬性文件中獲取并顯示了英文和簡體中文的國際化消息。

源碼分析總結(jié)

  1. 應(yīng)用啟動

    • 我們從MessageSourceAwareApplication啟動應(yīng)用廉沮,使用AnnotationConfigApplicationContext初始化Spring容器颓遏,并加載MyConfiguration配置。
  2. 容器初始化

    • AnnotationConfigApplicationContext的構(gòu)造函數(shù)中滞时,執(zhí)行了registerrefresh方法叁幢,其中refresh是最重要的,它觸發(fā)了容器的初始化和bean的創(chuàng)建過程坪稽。
  3. 消息源初始化

    • 在容器刷新的refresh方法中曼玩,首先確保了一個(gè)MessageSource bean存在鳞骤,這是通過initMessageSource方法完成的。如果沒有明確定義MessageSource bean黍判,Spring會提供一個(gè)默認(rèn)實(shí)現(xiàn)豫尽,確保應(yīng)用上下文總是有一個(gè)可用。
  4. bean實(shí)例化

    • 隨后顷帖,在refresh方法中美旧,通過調(diào)用finishBeanFactoryInitialization方法,容器開始實(shí)例化所有非延遲加載的單例bean贬墩。
  5. Bean的生命周期

    • 在bean的創(chuàng)建過程中榴嗅,Spring容器會確保所有的生命周期回調(diào)都被正確地執(zhí)行,其中最重要的是BeanPostProcessors陶舞。這些處理器提供了一個(gè)插件機(jī)制录肯,允許我們在bean的初始化前后執(zhí)行自定義的邏輯。
  6. 處理Aware接口

    • ApplicationContextAwareProcessor是一個(gè)特殊的BeanPostProcessor吊说,它關(guān)心那些實(shí)現(xiàn)了"Aware"接口的beans论咏。對于實(shí)現(xiàn)了MessageSourceAware的beans,該處理器會自動注入應(yīng)用上下文的MessageSource颁井。
  7. 消息檢索

    • 在我們的MyMessageSourceAware類中厅贪,已經(jīng)成功地獲取了MessageSource的引用。然后雅宾,我們調(diào)用其getMessage方法养涮,從屬性文件中檢索并打印兩個(gè)國際化的消息。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末眉抬,一起剝皮案震驚了整個(gè)濱河市贯吓,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蜀变,老刑警劉巖悄谐,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異库北,居然都是意外死亡爬舰,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門寒瓦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來情屹,“玉大人,你說我怎么就攤上這事杂腰±悖” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長惜颇。 經(jīng)常有香客問我皆刺,道長,這世上最難降的妖魔是什么官还? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任芹橡,我火速辦了婚禮毒坛,結(jié)果婚禮上望伦,老公的妹妹穿的比我還像新娘。我一直安慰自己煎殷,他們只是感情好屯伞,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著豪直,像睡著了一般劣摇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上弓乙,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天末融,我揣著相機(jī)與錄音,去河邊找鬼暇韧。 笑死勾习,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的懈玻。 我是一名探鬼主播巧婶,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼涂乌!你這毒婦竟也來了艺栈?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤湾盒,失蹤者是張志新(化名)和其女友劉穎湿右,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體罚勾,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡诅需,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了荧库。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片堰塌。...
    茶點(diǎn)故事閱讀 39,696評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖分衫,靈堂內(nèi)的尸體忽然破棺而出场刑,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布牵现,位于F島的核電站铐懊,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏瞎疼。R本人自食惡果不足惜科乎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望贼急。 院中可真熱鬧茅茂,春花似錦、人聲如沸太抓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽走敌。三九已至碴倾,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間掉丽,已是汗流浹背跌榔。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留捶障,地道東北人僧须。 一個(gè)月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像残邀,于是被迫代替她去往敵國和親皆辽。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容