有资源网

搜索
有资源网 首页 编程语言 查看内容

Spring源码阅读-ApplicationContext体系结构分析

2019-7-26 01:06| 发布者: admin| 查看: 301| 评论: 0

摘要: 目次 继承层次图概览 ConfigurableApplicationContext分析 AbstractApplicationContext GenericApplicationContext GenericXmlApplicationContext S

目次

  • 继承层次图概览
  • ConfigurableApplicationContext分析
    • AbstractApplicationContext
    • GenericApplicationContext
    • GenericXmlApplicationContext
    • StaticApplicationContext
    • ResourceAdapterApplicationContext
    • GenericGroovyApplicationContext
    • AnnotationConfigApplicationContext
    • AbstractRefreshableApplicationContext
    • AbstractRefreshableConfigApplicationContext
    • AbstractXmlApplicationContext
    • FileSystemXmlApplicationContext
    • ClassPathXmlApplicationContext
  • WebApplicationContext
    • ConfigurableWebApplicationContext
    • GenericWebApplicationContext
    • StaticWebApplicationContext
    • AbstractRefreshableWebApplicationContext
    • XmlWebApplicationContext
    • GroovyWebApplicationContext
    • AnnotationConfigWebApplicationContext
  • 本文头脑导图

上篇已经对IoC容器的计划举行了分析(Spring源码阅读-IoC容器解析),本篇将对ApplicationContext经典的继承层次图举行详细的分析,在心中形成一个大抵的印象,以便反面一步步调试源码的时间,不会太眼花缭乱。让我们一步步的前进吧...

继承层次图概览

使用IDEA的继承层次工具生成如下的图(选中ApplicationContext --> Ctrl+H):

(温馨提示:双击可查看高清大图)

编程语言-Spring源码阅读-ApplicationContext体系结构分析(1)

从上图能很清晰的看出,ApplicationContext的子接口分为两个部门:

  • ConfigurableApplicationContext:大部门的应用上下文都实现了该接口
  • WebApplicationContext:在web应用步伐中使用

ConfigurableApplicationContext分析

从上面的类的继承层次图能看到,ConfigurableApplicationContext是比较上层的一个接口,该接口也是比较紧张的一个接口,险些所有的应用上下文都实现了该接口。该接口在ApplicationContext的基础上提供了配置应用上下文的本领,别的提供了生命周期的控制本领。先看一下该接口的继承关系图(为了更加简便,去掉了ApplicationContext继承的接口):

(温馨提示:双击可查看高清大图)

编程语言-Spring源码阅读-ApplicationContext体系结构分析(2)

Closeable接口用于关闭应用上下文,开释所有的资源和锁,这也包罗摧毁所有缓存的单例的bean,常见的try-with-resources用法如下,执行完try体中的代码后会自动的调用close方法:

try (ConfigurableApplicationContext cac = ...) {
    // 编写代码 
    ...
}

Lifecycle界说了启动/停止生命周期的控制的一些方法,此中的方法如下:

void start(); // 启动组件
void stop(); // 停止组件
boolean isRunning(); // 组件是否正在运行

接下来看一下ConfigurableApplicationContext中的方法:

void setId(String id); // 设置应用上下文唯一的id
void setParent(ApplicationContext parent); // 设置应用步伐上下文的父级
void setEnvironment(ConfigurableEnvironment environment); // 设置应用上下文的环境
ConfigurableEnvironment getEnvironment();  // 获取应用上下文的环境
// 添加一个新的BeanFactoryPostProcessor
void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor);
// 添加应用步伐监听器
void addApplicationListener(ApplicationListener listener);
// 添加协议解析器,大概会覆盖默认的规则
void addProtocolResolver(ProtocolResolver resolver);
// 加载大概刷新配置
void refresh() throws BeansException, IllegalStateException;
// 向JVM runtime注册一个关闭钩子,JVM关闭时关闭这个上下文
void registerShutdownHook();
// 应用步伐上问下是否是激活状态
boolean isActive();
// 获取应用上下文内部的bean factory
ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;

上面的这些方法根本上是提供了对某些特性的实现举行支撑的方法。

看了这么多方法,下面看一下ApplicationContext的抽象的实现。

AbstractApplicationContext

AbstractApplicationContextApplicationContext接口的抽象实现,这个抽象类仅仅是实现了公共的上下文特性。这个抽象类使用了模板方法计划模式,必要具体的实现类去实现这些抽象的方法。对相关接口的实现如下:

  • ApplicationContext接口的实现
  • ConfigurableApplicationContext接口的实现
  • BeanFactory接口的实现
  • ListableBeanFactory接口的实现
  • HierarchicalBeanFactory接口的实现
  • MessageSource接口的实现
  • ResourcePatternResolver的实现
  • Lifecycle接口的实现

本文不会详细的讲解这个类中的具体的实现细节,反面会有更加的详细的先容。下面看下里面的抽象方法:

// 刷新BeanFactory,用于执行实际的配置加载,该方法在其他的初始化工作之前被refresh()方法调用
protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;
// 关闭BeanFactory,用于开释内部使用的BeanFactory·
protected abstract void closeBeanFactory();
// 获取内部使用的BeanFactory
public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;

那么对必要实现的方法经过抽象后,只剩下少量的必要子类去实现的方法。

GenericApplicationContext

GenericApplicationContext继承自AbstractApplicationContext,是为通用目的计划的,它能加载各种配置文件,比方xml,properties等等。它的内部持有一个DefaultListableBeanFactory的实例,实现了BeanDefinitionRegistry接口,以便答应向其应用任何bean的界说的读取器。为了能够注册bean的界说,refresh()只答应调用一次。常见的使用如下:

GenericApplicationContext ctx = new GenericApplicationContext();
XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(ctx);
xmlReader.loadBeanDefinitions(new ClassPathResource("applicationContext.xml"));
PropertiesBeanDefinitionReader propReader = new PropertiesBeanDefinitionReader(ctx);
propReader.loadBeanDefinitions(new ClassPathResource("otherBeans.properties"));
ctx.refresh();

MyBean myBean = (MyBean) ctx.getBean("myBean");
..

这个类的实现没有太多必要注意的地方,必要注意的有两点:

  • 内部使用的DefaultListableBeanFactory的实例,提供了一些方法来配置该实例,比方是否答应bean界说的覆盖、是否答应bean之间的循环应用等等。
  • 该类实现了BeanDefinitionRegistry,bean的界说注册。以便能通过BeanDefinitionReader读取bean的配置,并注册。BeanDefinitionRegistry接口的实现是直接使用内部的DefaultListableBeanFactory的实例。

GenericXmlApplicationContext

GenericXmlApplicationContext继承自GenericApplicationContext,内置了对XML的支持。它非常的方便和灵活,是ClassPathXmlApplicationContextFileSystemXmlApplicationContext的一种替换品。可以发现,它的内部有一个XmlBeanDefinitionReader的实例,专门用于处置惩罚XML的配置。

StaticApplicationContext

StaticApplicationContext继承自GenericApplicationContext,主要用于编程式的注入bean和消息,而不是从外部的配置源读取bean的界说。主要是在测试时非常有用。通过阅读源代码可以看到,它的内部有一个StaticMessageSource的实例,使用addMessage方法添加消息。每次在编程式的注入bean时,都会创建一个GenericBeanDefinition的实例。

ResourceAdapterApplicationContext

ResourceAdapterApplicationContext继承自GenericApplicationContext,是为JCA(J2EE Connector Architecture)的ResourceAdapter计划的,主要用于通报BootstrapContext的实例给实现了BootstrapContextAware接口且由spring管理的bean。覆盖了postProcessBeanFactory方法来实现此功能。

GenericGroovyApplicationContext

GenericGroovyApplicationContext继承自GenericApplicationContext,实现了GroovyObject接口以便能够使用点的语法(.xx)取代getBean方法来获取bean。它主要用于Groovy bean的界说,与GenericXmlApplicationContext一样,它也能解析XML格式界说的bean。内部使用GroovyBeanDefinitionReader来完成groovy脚本和XML的解析。

AnnotationConfigApplicationContext

AnnotationConfigApplicationContext继承自GenericApplicationContext,提供了注解配置(比方:Configuration、Component、inject等)和类路径扫描(scan方法)的支持,可以使用register(Class... annotatedClasses)来注册一个一个的举行注册。实现了AnnotationConfigRegistry接口,来完成对注册配置的支持,只有两个方法:register和scan。内部使用AnnotatedBeanDefinitionReader来完成注解配置的解析,使用ClassPathBeanDefinitionScanner来完成类路径下的bean界说的扫描。

AbstractRefreshableApplicationContext

AbstractRefreshableApplicationContext继承自AbstractApplicationContext,支持多次举行刷新(多次调用refresh方法),每次刷新时在内部创建一个新的bean工厂的实例。子类仅仅必要实现loadBeanDefinitions方法,该方法在每次刷新时都会调用。

AbstractRefreshableConfigApplicationContext

AbstractRefreshableConfigApplicationContext继承自AbstractRefreshableApplicationContext,添加了对指定的配置文件路径的公共的处置惩罚,可以把他看作基于XML的应用上下文的基类。实现了如下的两个接口:

  • BeanNameAware用于设置上下文的bean的名称,只有一个方法:void setBeanName(String name)
  • InitializingBean用于上下文一切就绪后,假如还未刷新,那么就执行刷新操作,只有一个方法:void afterPropertiesSet()

AbstractXmlApplicationContext

AbstractXmlApplicationContext继承自AbstractRefreshableConfigApplicationContext,用于描绘包含能被XmlBeanDefinitionReader所明确的bean界说的XML文档。子类只必要实现getConfigResourcesgetConfigLocations来提供配置文件资源。

FileSystemXmlApplicationContext

FileSystemXmlApplicationContext继承自AbstractXmlApplicationContext,用于解析文件系统中XML配置文件。文件的路径可以是具体的文件路径,比方:xxx/application.xml,也可以是ant风格的配置,比方:xxx/*-context.xml。

看下面的通过文件路径来获取资源的代码:

@Override
protected Resource getResourceByPath(String path) {
    if (path.startsWith("/")) {
        path = path.substring(1);
    }
    return new FileSystemResource(path);
}

文件路径前面的/会被去掉,无论是否路径前面是否加上/,文件路径都会解析成相对路径,即基于JVM的当前工作路径。获取到的资源对象是FileSystemResource,用于处置惩罚文件系统相关的资源。

ClassPathXmlApplicationContext

ClassPathXmlApplicationContext继承自AbstractXmlApplicationContext,和FileSystemXmlApplicationContext雷同,只不过ClassPathXmlApplicationContext是用于处置惩罚类路径下的XML配置文件。文件的路径可以是具体的文件路径,比方:xxx/application.xml,也可以是ant风格的配置,比方:xxx/*-context.xml。

WebApplicationContext

该接口提供了在web应用中的配置,接口提供了一个ServletContext getServletContext()用来获取ServletContext对象。该接口会和ServletContext的一个属性举行绑定,这个属性就是ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE。界说了三个作用域的名称:SCOPE_REQUEST,SCOPE_SESSION,SCOPE_APPLICATION。在工厂中的bean的名称:SERVLET_CONTEXT_BEAN_NAME。ServletContext初始化参数名称:CONTEXT_PARAMETERS_BEAN_NAME。在工厂中ServletContext属性值环境bean的名称:CONTEXT_ATTRIBUTES_BEAN_NAME

ConfigurableWebApplicationContext

ConfigurableWebApplicationContext继承自WebApplicationContextConfigurableApplicationContext,提供了web应用上下文的可配置的本领。相关接口界说如下:

// 设置web应用上下文的ServletContext
void setServletContext(@Nullable ServletContext servletContext);
// 设置web应用上下文的ServletConfig
void setServletConfig(@Nullable ServletConfig servletConfig);
// 获取web应用上下文的ServletConfig
ServletConfig getServletConfig();
// 设置web应用上下文的命名空间
void setNamespace(@Nullable String namespace);
// 获取web应用上下文的命名空间
String getNamespace();
// 以初始化参数的情势设置web应用上下文的配置文件位置
void setConfigLocation(String configLocation);
// 设置web应用上下文的配置文件的位置
void setConfigLocations(String... configLocations);
// 获取web应用上下文的配置文件位置
String[] getConfigLocations();

上面的接口主要都是一些设置大概获取的方法,在web应用上下文中必要用到的一些东西。

GenericWebApplicationContext

GenericWebApplicationContext继承自GenericApplicationContext,实现了ConfigurableWebApplicationContextThemeSource接口。该类计划的目的不是在web.xml中举行声明式的安装,而是编程式的安装,比方使用WebApplicationInitializers来构建内嵌的上下文。该接口在ConfigurableWebApplicationContext的内容都是一个伪实现,调用此中的大多数方法都会抛出非常。你大概注意到了,他实现了ThemeSource接口,那么他有什么用呢?字面意思是主题源,它计划的目的主要是用于消息的国际化。

StaticWebApplicationContext

StaticWebApplicationContext继承自StaticApplicationContext实现了ConfigurableWebApplicationContextThemeSource接口。该接口主要是用在测试的环境,不消于产品环境。

AbstractRefreshableWebApplicationContext

GenericWebApplicationContext继承自AbstractRefreshableConfigApplicationContext,实现了ConfigurableWebApplicationContextThemeSource接口,主要是用于web环境下。在web步伐启动的时间,提供一个configLocations属性,通过ConfigurableWebApplicationContext接口来举行填充。子类化这个接口是很简单的,所有你所必要做的事情就是实现loadBeanDefinitions方法,来实现你自己的bean界说的加载逻辑。

XmlWebApplicationContext

XmlWebApplicationContext继承自AbstractRefreshableWebApplicationContext,接受能被XmlBeanDefinitionReader所明确的XML文档配置。对于根上下文,默认的配置文件路径是/WEB-INF/applicationContext.xml,对于命名空间为test-servlet的上下文,默认的配置文件路径是/WEB-INF/test-servlet.xml(就像servlet-name为test的DispatcherServlet实例)。

默认的配置文件路径处置惩罚的代码如下:

protected String[] getDefaultConfigLocations() {
    if (getNamespace() != null) {
        return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};
    }
    else {
        return new String[] {DEFAULT_CONFIG_LOCATION};
    }
}

和其他的上下文一样,bean界说的加载也是在void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)方法中,使用的是XmlBeanDefinitionReader

GroovyWebApplicationContext

GroovyWebApplicationContext继承自AbstractRefreshableWebApplicationContext,实现了GroovyObject接口,接受能被GroovyBeanDefinitionReader所明确的groovy bean界说脚本和XML文档配置。对于web环境,根本上是和GenericGroovyApplicationContext是等价的。对于根上下文,默认的配置文件路径是/WEB-INF/applicationContext.groovy,对于命名空间为test-servlet的上下文,默认的配置文件路径是/WEB-INF/test-servlet.xml(就像servlet-name为test的DispatcherServlet实例)。

默认的配置文件路径处置惩罚的代码如下:

protected String[] getDefaultConfigLocations() {
    if (getNamespace() != null) {
        return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};
    }
    else {
        return new String[] {DEFAULT_CONFIG_LOCATION};
    }
}

和其他的上下文一样,bean界说的加载也是在void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)方法中,使用的是GroovyBeanDefinitionReader

AnnotationConfigWebApplicationContext

AnnotationConfigWebApplicationContext继承自AbstractRefreshableWebApplicationContext

接受注解的类作为输入(特殊的@Configuration注解类,一般的@Component注解类,与JSR-330兼容的javax.inject注解)。答应一个一个的注入,同样也能使用类路径扫描。对于web环境,根本上是和AnnotationConfigApplicationContext等价的。使用AnnotatedBeanDefinitionReader来对注解的bean举行处置惩罚,使用ClassPathBeanDefinitionScanner来对类路径下的bean举行扫描。

部门代码如下:

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) {
    AnnotatedBeanDefinitionReader reader = getAnnotatedBeanDefinitionReader(beanFactory);
    ClassPathBeanDefinitionScanner scanner = getClassPathBeanDefinitionScanner(beanFactory);
    ...
    if (!this.annotatedClasses.isEmpty()) {
       ....
        reader.register(ClassUtils.toClassArray(this.annotatedClasses));
    }

    if (!this.basePackages.isEmpty()) {
        ....
        scanner.scan(StringUtils.toStringArray(this.basePackages));
    }

    String[] configLocations = getConfigLocations();
    if (configLocations != null) {
        for (String configLocation : configLocations) {
            try {
                Class clazz = ClassUtils.forName(configLocation, getClassLoader());
                if (logger.isTraceEnabled()) {
                    logger.trace("Registering [" + configLocation + "]");
                }
                reader.register(clazz);
            }
            catch (ClassNotFoundException ex) {
                ....
                }
            }
        }
    }
}

本文头脑导图

编程语言-Spring源码阅读-ApplicationContext体系结构分析(3)


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

鲜花

握手

雷人

路过

鸡蛋

最新评论

返回顶部