spring高级容器之ApplicationContext

关于之前的文章我们都是围绕spring的低级容器beanFactory展开,我们都知道spring的容器有两种,一种是beanFactory,俗称低级容器,在实际的开发中我们常用的xmlBeanFactory,当然低级的容器是无法满足日常的开发,那么ApplicationContext高级容器诞生了,在低级容器的基础上进行了拓展,如:事件的发布bean的生命周期的管理和多资源加载等,我们常用的是ClassPathXmlApplicationContext 以及fileSystemXmlApplicationContext等,在接下来的篇章中,我们将学习ApplicationContext的东西,关于它有以下的几个功能:

  • 继承于MessageSource,为我们提供了提供国际化的标准访问策略.
  • 继承ApplicationEventPublisher ,提供强大的事件机制.
  • 扩展 ResourceLoader,可以用来加载多个 Resource,可以灵活访问不同的资源.
  • 对 Web 应用的支持

我们来看ApplicationContext的家族图:

ApplicationContext.png

简单的来看一下家族图的成员:

  • BeanFactory:spring管理bean的顶级接口,俗称基本容器,在该容器下有两个子接口分别是HierarchicalBeanFactory和ListableBeanFactory.
  • HierarchicalBeanFactory:是一个具有层级关系的BeanFactory,其中包含一个很重要的属性为parentBeanFactory.
  • ListableBeanFactory:是以枚举的方式列举当前beanFactory中所有的bean对象.
  • ApplicationEventPublisher:是一个封装事件发布的接口.
  • ResourceLoader:是spring用来加载资源的顶级接口,主要是从给定的资源中加载文件.
  • MessageSource:是一个解析message的顶级策略接口,在平时的开发中我们几乎用不到.
  • EnvironmentCapable:用于获取当前容器的上下文环境.

在实际的开发中我们很少用到ApplicationContext,一般会用它的实现类,具体有二种直接实现类型,分别是:

  • WebApplicationContext
public interface WebApplicationContext extends ApplicationContext {

  ServletContext getServletContext();
}

该接口中只有一个方法,其主要的作用是获取当前servlet的上下文信息.

  • ConfigurableApplicationContext
  ''''''
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {

/**
 * 给applicationContext设置唯一id
 * @param id
 */
void setId(String id);
/**
 * 给applicationContext设置parent
 *  父类不应该被修改,如果创建的对象不可用时,则应该在构造函数外部设置它
 * @param parent
 */
void setParent(@Nullable ApplicationContext parent);
/**
 * 设置environment
 * @param environment
 */
void setEnvironment(ConfigurableEnvironment environment);
   /**
 * 获取environment
 */
ConfigurableEnvironment getEnvironment();
  /**
 * 给applicationContext添加BeanFactoryPostProcessor
 * @param postProcessor
 */
void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor);
/**
 * 给applicationContext添加ApplicationListener
 * @param listener
 */
void addApplicationListener(ApplicationListener<?> listener);
/**
 * 给applicationContext添加ProtocolResolver
 * @param resolver
 */
void addProtocolResolver(ProtocolResolver resolver);
/**
 * 加载或者刷新配置方法(重要)
 * @throws BeansException
 * @throws IllegalStateException
 */
void refresh() throws BeansException, IllegalStateException;
  /**
 * 注册shutdown hook
 */
void registerShutdownHook();
/**
 * 关闭ApplicationContext
 */
void close();
/**
 * 判断ApplicationContext是否处于激活状态
 * @return
 */
boolean isActive();
/**
 * 获取当前上下文的BeanFactory
 * @return
 * @throws IllegalStateException
 */
ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
}

从ConfigurableApplicationContext中可以看出,提供的好多方法都是给ApplicationContext进行相关的配置过程.同时我们还看到ConfigurableApplicationContext实现了Lifecycle和Closeable接口.

  • 其中Lifecycle是用来管理当前context的生命周期进行管理,于此提供了#start()和#stop()等
  • Closeable : 是JDK提供的一个标准释放资源的组件
ConfigurableWebApplicationContext

在applicationContext的子类中,ConfigurableWebApplicationContext是WebApplicationContext和ConfigurableApplicationContext的共有的子接口,其主要的作用是针对WebApplicationContext容器的可配置 和可管理的一个接口,我们来看该接口的代码:

public interface ConfigurableWebApplicationContext extends         WebApplicationContext, ConfigurableApplicationContext {
void setServletContext(@Nullable ServletContext servletContext);
void setServletConfig(@Nullable ServletConfig servletConfig);
  ServletConfig getServletConfig();
  void setNamespace(@Nullable String namespace);
  String getNamespace();
  void setConfigLocation(String configLocation);
  void setConfigLocations(String... configLocations);
  String[] getConfigLocations();
}

上述就是ConfigurableWebApplicationContext中的方法,一些方法是共有的,方简单易懂,大家知道就可以了,接着我们来看APPlicationContext中的另外一个常用方法ClassPathXmlApplicationContext,该方法是从当前容器的classPath下读取我们的配置文件,所以我们的配置文件一定要放在resource文件夹下:

ClassPathXmlApplicationContext
ClassPathXmlApplicationContext.png

接下来我们来看一下常用的接口

  • MessageSource
  public interface MessageSource {
      //获取message
@Nullable
  String getMessage(String code, @Nullable Object[] args, @Nullable     String defaultMessage, Locale locale);
  String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException;
  String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;
  }

我们可以看到的是在MessageSource主要是定义了获取message的策略方法#getMessage(...),该接口的直接实现类是AbstractApplicationContext类,我们来看一下代码:

  /** MessageSource we delegate our implementation of this interface to. */
@Nullable
private MessageSource messageSource;

//---------------------------------------------------------------------
// Implementation of MessageSource interface
//---------------------------------------------------------------------

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

这里并不是真正的实现逻辑的地方,我们接着看:

public final String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException {
    String msg = getMessageInternal(code, args, locale);
    if (msg != null) {
        return msg;
    }
    String fallback = getDefaultMessage(code);
    if (fallback != null) {
        return fallback;
    }
    throw new NoSuchMessageException(code, locale);
}

原来真正的实现逻辑是在AbstractMessageSource类中

  • ApplicationEventPublisher

我们知道ApplicationEventPublisher是spring用来处理事件发布的功能接口,该接口提供了一个#publishEvent(Object event, ...)方法用来通知应用程序中注册的所有的监听器,该方法同样也是在我们的AbstractApplicationContext类中实现,接下来我们来看代码实现:

  @Override
public void publishEvent(ApplicationEvent event) {
    publishEvent(event, null);
}

//给所有的监听器发布事件
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
    Assert.notNull(event, "Event must not be null");

    // Decorate event as an ApplicationEvent if necessary
    //事件类型
    ApplicationEvent applicationEvent;
    if (event instanceof ApplicationEvent) {
        applicationEvent = (ApplicationEvent) event;
    }
    else {
        applicationEvent = new PayloadApplicationEvent<>(this, event);
        if (eventType == null) {
            eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
        }
    }

    // Multicast right now if possible - or lazily once the multicaster is initialized
    if (this.earlyApplicationEvents != null) {
        this.earlyApplicationEvents.add(applicationEvent);
    }
    else {
        getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
    }

    // Publish event via parent context as well...
    if (this.parent != null) {
        if (this.parent instanceof AbstractApplicationContext) {
            ((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
        }
        else {
            this.parent.publishEvent(event);
        }
    }
}
  • 主要是针对不同的事件类型进行不同的处理
  • ResourcePatternResolver

ResourcePatternResolver主要是继承接口ResourceLoader,其主要的作用是将我们的location解析为Resource对象的策略接口,其中提供了一个很重要的方法#getResources(String locationPattern)方法,在AbstractApplicationContext类中有具体的实现过程,来看代码:

/** ResourcePatternResolver used by this context. */
private ResourcePatternResolver resourcePatternResolver;
//---------------------------------------------------------------------
// Implementation of ResourcePatternResolver interface
//---------------------------------------------------------------------

@Override
public Resource[] getResources(String locationPattern) throws IOException {
    return this.resourcePatternResolver.getResources(locationPattern);
}

这里就不在深究了,其实关于更深的实现过程是在PathMatchingResourcePatternResolver来完成,该类是ResourcePatternResolver的实现类

  • EnvironmentCapable
  public interface EnvironmentCapable {

/**
 * Return the {@link Environment} associated with this component.
 */
Environment getEnvironment();

}

该接口中只有一个# getEnvironment()的方法,其主要是用来获取当前spring的上下文环境实例Environment.我们来看一下实现的过程,代码如下:

  /**
 * 该方法主要的作用是从当前配置文件中获取当前容器的上下文
 * @return
 */
@Override
public ConfigurableEnvironment getEnvironment() {
    //如果默认的为null,需要初始化
    if (this.environment == null) {
        this.environment = createEnvironment();
    }
    return this.environment;
}

  /**
 * 创建一个新的StandardEnvironment
 * @return
 */
protected ConfigurableEnvironment createEnvironment() {
    return new StandardEnvironment();
}

其中我们发现最后是创建一个StandardEnvironment的实体,该类主要是表示适用于非 WEB 应用的 Environment.

  • Lifecycle

该接口提供了管理bean的生命周期的方法,在AbstractApplicationContext方法中有具体的实现过程,我们来看代码:

//---------------------------------------------------------------------
// Implementation of Lifecycle interface
//---------------------------------------------------------------------
  /** LifecycleProcessor for managing the lifecycle of beans within this context. */
@Nullable
private LifecycleProcessor lifecycleProcessor;
@Override
public void start() {
    getLifecycleProcessor().start();
    publishEvent(new ContextStartedEvent(this));
}

@Override
public void stop() {
    getLifecycleProcessor().stop();
    publishEvent(new ContextStoppedEvent(this));
}

@Override
public boolean isRunning() {
    return (this.lifecycleProcessor != null && this.lifecycleProcessor.isRunning());
}

在实现类中提供了start以及stop方法,分别通过不同的事件来处理如ContextStartedEvent和ContextStoppedEvent事件.

  • Closable

该接口主要的作用是释放和关闭资源,给我们提供了#close()方法.

public void close() {
    synchronized (this.startupShutdownMonitor) {
        doClose();
        // If we registered a JVM shutdown hook, we don't need it anymore now:
        // We've already explicitly closed the context.
        if (this.shutdownHook != null) {
            try {
                Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
            }
            catch (IllegalStateException ex) {
                // ignore - VM is already shutting down
            }
        }
    }
}

这里我们发现调用了#doClose()的方法进行逻辑的处理,来看代码:

protected void doClose() {
    // Check whether an actual close attempt is necessary...
    if (this.active.get() && this.closed.compareAndSet(false, true)) {
        if (logger.isDebugEnabled()) {
            logger.debug("Closing " + this);
        }

        LiveBeansView.unregisterApplicationContext(this);

        try {
            // Publish shutdown event.
            publishEvent(new ContextClosedEvent(this));
        }
        catch (Throwable ex) {
            logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
        }

        // Stop all Lifecycle beans, to avoid delays during individual destruction.
        if (this.lifecycleProcessor != null) {
            try {
                this.lifecycleProcessor.onClose();
            }
            catch (Throwable ex) {
                logger.warn("Exception thrown from LifecycleProcessor on context close", ex);
            }
        }

        // Destroy all cached singletons in the context's BeanFactory.
        destroyBeans();

        // Close the state of this context itself.
        closeBeanFactory();

        // Let subclasses do some final clean-up if they wish...
        onClose();

        // Reset local application listeners to pre-refresh state.
        if (this.earlyApplicationListeners != null) {
            this.applicationListeners.clear();
            this.applicationListeners.addAll(this.earlyApplicationListeners);
        }

        // Switch to inactive.
        this.active.set(false);
    }
}
  • InitializingBean

InitializingBean主要是提供了bean的初始化方式,在该接口中只有afterPropertiesSet()方法,如果bean实现了了该接口,那么
就通过该方法来实现bean的初始化过程

public void afterPropertiesSet() {
    if (!isActive()) {
        refresh();
    }
}

该方法位于AbstractRefreshableConfigApplicationContext类中,代码中的refresh()方法是整个applicationContext容器的核心方法,详解我们后面来说.

  • BeanNameAware

该接口我们之前也有所了解,在我们初始化bean之前调用各种Aware接口来设置对应的xxxAware属性,其中这里的BeanNameAware就是其中的一个

  /**
 * Sets the id of this context to the bean name by default,
 * for cases where the context instance is itself defined as a bean.
 */
@Override
public void setBeanName(String name) {
    if (!this.setIdCalled) {
        super.setId(name);
        setDisplayName("ApplicationContext '" + name + "'");
    }
}
  • 总结

该篇主要讲了spring的applicationContext的结构,以及我们了解了applicationContext的众多核心接口,在下篇我们来讲一个很重的refash()方法...

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,376评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,126评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,966评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,432评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,519评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,792评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,933评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,701评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,143评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,488评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,626评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,292评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,896评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,742评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,324评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,494评论 2 348

推荐阅读更多精彩内容