Spring官网阅读(十三)ApplicationContext详解(下)(2)

简介: Spring官网阅读(十三)ApplicationContext详解(下)(2)

ApplicationContext体系汇总


ApplicationContext整体可以分为两个体系,一个就是web体系,另外一个就是非web体系。


非web体系


微信图片_20221113100443.png


1、ConfigurableApplicationContext


ApplicationContext接口中的方法比较简单,之前我们也一一分析它继承的接口以及它所具有的功能。并且ApplicationContext接口的方法都是只读的,不能对当前的容器做任何改变。而ConfigurableApplicationContext接口在ApplicationContext的基础上增加了很多进行配置的方法,比如添加事件监听器,添加后置处理器等等。

public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {
  // 配置路径的分隔符
  String CONFIG_LOCATION_DELIMITERS = ",; \t\n";
  String CONVERSION_SERVICE_BEAN_NAME = "conversionService";
  String LOAD_TIME_WEAVER_BEAN_NAME = "loadTimeWeaver";
  String ENVIRONMENT_BEAN_NAME = "environment";
  String SYSTEM_PROPERTIES_BEAN_NAME = "systemProperties";
  String SYSTEM_ENVIRONMENT_BEAN_NAME = "systemEnvironment";
  //设置此应用程序上下文的唯一ID。
  void setId(String id);
    //设置父容器,设置后不能再改了
  void setParent(@Nullable ApplicationContext parent);
    //设置environment  此处为ConfigurableEnvironment 也是可以配置的应用上下文
  void setEnvironment(ConfigurableEnvironment environment);
    // 此处修改父类返回值为ConfigurableEnvironment 
  @Override
  ConfigurableEnvironment getEnvironment();
  //添加一个新的BeanFactoryPostProcessor(refresh()的时候会调用的)
  void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor);
    // 添加一个事件监听器
  void addApplicationListener(ApplicationListener<?> listener);
    // 注册协议处理器  允许处理额外的资源协议
  void addProtocolResolver(ProtocolResolver resolver);
  //加载或刷新配置的持久表示  最最最重要的一个方法
  //表示可以是xml、可以是注解、可以是外部资源文件等等。。。。
  // 这个方法执行完成后,所有的单例Bean都已经被实例化,Bean工厂肯定也就被创建好了
  void refresh() throws BeansException, IllegalStateException;
  //JVM运行时注册一个关闭挂钩,在关闭JVM时关闭此上下文,除非此时已经关闭
  void registerShutdownHook();
  //关闭此应用程序上下文,释放实现可能持有的所有资源和锁  包括一些销毁、释放资源操作
  @Override
  void close();
    //标识上下文是否激活 refresh()后就会激活
  boolean isActive();
    // 返回此上下文内部的Bean工厂,可以用来访问底层工厂的特定功能。通过此工厂可以设置和验证所需的属性、自定义转换服务
  // 备注:父类方法为获得AutowireCapableBeanFactory接口,而此处的ConfigurableListableBeanFactory可配置、可列出Bean的工厂是它的子类
  ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
}

2、AbstractApplicationContext

public abstract class AbstractApplicationContext extends DefaultResourceLoader
    implements ConfigurableApplicationContext {
  // 这个类实现了ConfigurableApplicationContext,具备了上面接口大部分功能,
    // 但是他没有实现getBeanFactory()方法,这个方法留待子类实现,所以它自己没有实际的管理Bean的能力,只是定义了一系列规范
}

3、AbstractRefreshableApplicationContext

public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {
    // 碰到重复的Bean时,是否允许覆盖原先的BeanDefinition
  @Nullable
  private Boolean allowBeanDefinitionOverriding;
    // 是否允许循环引用
  @Nullable
  private Boolean allowCircularReferences;
    // 默认持有一个DefaultListableBeanFactory
  @Nullable
  private DefaultListableBeanFactory beanFactory;
  // 对内部工厂进行操作时所采用的锁
  private final Object beanFactoryMonitor = new Object();
  public AbstractRefreshableApplicationContext() {
  }
  public AbstractRefreshableApplicationContext(@Nullable ApplicationContext parent) {
    super(parent);
  }
  public void setAllowBeanDefinitionOverriding(boolean allowBeanDefinitionOverriding) {
    this.allowBeanDefinitionOverriding = allowBeanDefinitionOverriding;
  }
  public void setAllowCircularReferences(boolean allowCircularReferences) {
    this.allowCircularReferences = allowCircularReferences;
  }
    // 刷新Bean工厂,如果当前上下文中已经存在一个容器的话,会先销毁容器中的所有Bean,然后关闭Bean工厂
    // 之后在重新创建一个DefaultListableBeanFactory
  @Override
  protected final void refreshBeanFactory() throws BeansException {
    if (hasBeanFactory()) {
      destroyBeans();
      closeBeanFactory();
    }
    try {
      DefaultListableBeanFactory beanFactory = createBeanFactory();
      beanFactory.setSerializationId(getId());
      customizeBeanFactory(beanFactory);
      loadBeanDefinitions(beanFactory);
      synchronized (this.beanFactoryMonitor) {
        this.beanFactory = beanFactory;
      }
    }
    catch (IOException ex) {
      throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    }
  }
  @Override
  protected void cancelRefresh(BeansException ex) {
    synchronized (this.beanFactoryMonitor) {
      if (this.beanFactory != null) {
        this.beanFactory.setSerializationId(null);
      }
    }
    super.cancelRefresh(ex);
  }
  @Override
  protected final void closeBeanFactory() {
    synchronized (this.beanFactoryMonitor) {
      if (this.beanFactory != null) {
        this.beanFactory.setSerializationId(null);
        this.beanFactory = null;
      }
    }
  }
  protected final boolean hasBeanFactory() {
    synchronized (this.beanFactoryMonitor) {
      return (this.beanFactory != null);
    }
  }
    // 复写了getBeanFactory,默认返回的是通过createBeanFactory创建的一个DefaultListableBeanFactory
  @Override
  public final ConfigurableListableBeanFactory getBeanFactory() {
    synchronized (this.beanFactoryMonitor) {
      if (this.beanFactory == null) {
        throw new IllegalStateException("BeanFactory not initialized or already closed - " +
            "call 'refresh' before accessing beans via the ApplicationContext");
      }
      return this.beanFactory;
    }
  }
  protected DefaultListableBeanFactory createBeanFactory() {
    return new DefaultListableBeanFactory(getInternalParentBeanFactory());
  }
  .......
  // 提供了一个抽象的加载BeanDefinition的方法,这个方法没有具体实现,不同的配置方式需要进行不同的实现,
    // 到这里,配置的方式不能确定,既可能是以XML的方式,也可能是以java config的方式
    // 另外配置文件的加载方式也不能确定
  protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
      throws BeansException, IOException;
}

可以看到这个类可以进一步对上下文进行配置,例如进行是否开启循环引用,是否允许进行BeanDefinition的覆盖等等。另外它所提供的一个重要的功能就是使容器具备刷新的功能,换言之凡是需要刷新功能的容器都需要继承这个类。


4、AbstractRefreshableConfigApplicationContext

public abstract class AbstractRefreshableConfigApplicationContext extends AbstractRefreshableApplicationContext
    implements BeanNameAware, InitializingBean {
  // 这个变量代表了配置文件的路径,到这里配置的信息相比于其父类AbstractRefreshableApplicationContext做了进一步的明确,但是仍然不能确定是XML还是javaconfig,只能确定配置在configLocations里面
  @Nullable
  private String[] configLocations;
    .....
}

5、AbstractXmlApplicationContext

public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {
    // 是否进行XML类型的校验,默认为true
    private boolean validating = true;
    // .....
  @Override
  protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
    beanDefinitionReader.setEnvironment(this.getEnvironment());
    beanDefinitionReader.setResourceLoader(this);
    beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
    initBeanDefinitionReader(beanDefinitionReader);
    loadBeanDefinitions(beanDefinitionReader);
  }
  protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader) {
    reader.setValidating(this.validating);
  }
  protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
    Resource[] configResources = getConfigResources();
    if (configResources != null) {
      reader.loadBeanDefinitions(configResources);
    }
    String[] configLocations = getConfigLocations();
    if (configLocations != null) {
      reader.loadBeanDefinitions(configLocations);
    }
  }
  @Nullable
  protected Resource[] getConfigResources() {
    return null;
  }
}

可以看到这个类进一步对配置的加载做了进一步的明确,首先明确了配置的类型为XML,第二明确了要通过getConfigResources方法来加载需要的配置资源,但是并没有对这个方法做具体实现,因为对于Resource的定义,可能是通过classpath的方式,也可能是通过URL的方式,基于此又多了两个子类


1.ClassPathXmlApplicationContext,从classPath下加载配置文件

2.FileSystemXmlApplicationContext,基于URL的格式加载配置文件


6、GenericApplicationContext


这个类已经不是抽象类了,我们可以直接使用它。但是这个类有一个很大的缺点,它不能读取配置,需要我们手动去指定读取的方式及位置。其实从上文中的分析我们可以看出,从AbstractApplicationContext到AbstractXmlApplicationContext一步步明确了配置的加载方式,Spring通过这种类的继承将配置的加载分了很多层,我们可以从AbstractXmlApplicationContext的子类开始从任意以及进行扩展。


而GenericApplicationContext只实现了上下文的基本功能,并没有对配置做任何约束,所以在使用它的我们需要手动往其中注册BeanDefinition。这样虽然很灵活,但是也很麻烦,如果我们使用GenericApplicationContext可能需要进行下面这样的操作

GenericApplicationContext ctx = new GenericApplicationContext();
//使用XmlBeanDefinitionReader,这个地方我们甚至可以自己定义解析器,不使用Spring容器内部的
XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(ctx);
//加载ClassPathResource
xmlReader.loadBeanDefinitions(new ClassPathResource("applicationContext.xml"));
PropertiesBeanDefinitionReader propReader = new PropertiesBeanDefinitionReader(ctx);
propReader.loadBeanDefinitions(new ClassPathResource("otherBeans.properties"));
//调用Refresh方法
ctx.refresh();
//和其他ApplicationContext方法一样的使用方式
MyBean myBean = (MyBean) ctx.getBean("myBean");

平常开发中我们基本用不到这个东西


7、AnnotationConfigApplicationContext

public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {
  private final AnnotatedBeanDefinitionReader reader;
  private final ClassPathBeanDefinitionScanner scanner;
    .......
}

通过AnnotatedBeanDefinitionReader注册配置类,用ClassPathBeanDefinitionScanner扫描配置类上申明的路径,得到所有的BeanDefinition。然后其余的没啥了。这个我们经常使用,因为不用再需要xml文件了,使用@Configuration配置类即可,更加的方便。


web体系


微信图片_20221113101027.png

1、WebApplicationContext

public interface WebApplicationContext extends ApplicationContext {
  String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
  String SCOPE_REQUEST = "request";
  String SCOPE_SESSION = "session";
  String SCOPE_APPLICATION = "application";
  String SERVLET_CONTEXT_BEAN_NAME = "servletContext";
  String CONTEXT_PARAMETERS_BEAN_NAME = "contextParameters";
  String CONTEXT_ATTRIBUTES_BEAN_NAME = "contextAttributes";
  @Nullable
  ServletContext getServletContext();
}

定义了一堆常量,以及一个方法,约束了所有的web容器必须能返回一个Servlet的上下文(ServletContext)


2、ConfigurableWebApplicationContext

public interface ConfigurableWebApplicationContext extends WebApplicationContext, ConfigurableApplicationContext {
  String APPLICATION_CONTEXT_ID_PREFIX = WebApplicationContext.class.getName() + ":";
  String SERVLET_CONFIG_BEAN_NAME = "servletConfig";
  void setServletContext(@Nullable ServletContext servletContext);
  void setServletConfig(@Nullable ServletConfig servletConfig);
  @Nullable
  ServletConfig getServletConfig();
    // 设置及获取当前上下文的命名空间,命名空间用于区分不同的web容器的配置,在查找配置时会根据命名空间查找
    // 默认不进行命名空间配置,配置会在/WEB-INF/applicationContext.xml下查找
    // 如果配置了,会在/WEB-INF+"namespace"+/applicationContext.xml下查找
    // 根容器没有Namespace
  void setNamespace(@Nullable String namespace);
  @Nullable
  String getNamespace();
  void setConfigLocation(String configLocation);
  void setConfigLocations(String... configLocations);
  @Nullable
  String[] getConfigLocations();
}

可以看到使用这个类能指定上下文配置加载的位置


3、AbstractRefreshableWebApplicationContext

public abstract class AbstractRefreshableWebApplicationContext extends AbstractRefreshableConfigApplicationContext
    implements ConfigurableWebApplicationContext, ThemeSource {
    .......
}

首先可以看到这个类继承了AbstractRefreshableConfigApplicationContext,代表它需要从指定的位置加载配置,其次它首先了ConfigurableWebApplicationContext,所以它具有web容器的属性。


4、XmlWebApplicationContext

public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {
    public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";
    public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";
    public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";
  //  .......
    @Override
    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};
        }
    }
}

进一步指定了配置文件的加载形式


1.需要加载XML类型配置

2对于根容器,加载路径为/WEB-INF/applicationContext.xml

3.对于子容器,加载路径为/WEB-INF/+'namespace'+.xml,比如常用的dispatchServlet.xml


5、AnnotationConfigWebApplicationContext


指定了以注解的方式配置web容器


6、GenericWebApplicationContext


类比GenericApplicationContext,没有指定配置相关的任何东西,全手动


总结


从上面我们可以看到,整个一套体系下来不可谓不庞大,Spring在单一职责可以说做到了极致。不论是按功能分,比如HierarchicalBeanFactory,ListableBeanFactory,AutowireCapableBeanFactory就是按照不同功能拆分,或者是按照功能实现的层级划分,比如上面说到的配置文件的加载机制。对类之间的关系进行明确的分层,代表了整个体系会具备非常强大的扩展性,我们可以在每一步进行自己的扩展。这是让Spring能组件化开发,可插拔,变得如此优秀、普适的重要原因


到此,关于ApplicationContext相关的内容终于也可以告一段落了,代表着IOC已经结束了,粗略看了下官网,接下来还剩数据绑定,数据校验,类型转换以及AOP,任重而道远,加油吧!~


相关文章
|
4月前
|
Java Spring
Spring 源码阅读 72:基于 CGLIB 的 AOP 代理的原理(2)- 拦截器的查找与执行
【1月更文挑战第7天】本文分析了基于 CGLIB 的 AOP 代理如何查找和执行拦截器链,其主要的逻辑在 DynamicAdvisedInterceptor 的intercept方法执行。
35 1
|
3月前
|
Java 应用服务中间件 Spring
Spring5源码(50)-SpringMVC源码阅读环境搭建
Spring5源码(50)-SpringMVC源码阅读环境搭建
42 0
|
4月前
|
缓存 Java Spring
Spring 源码阅读 66:基于 JDK 的 AOP 代理如何获取拦截器链(4)- 将 Advice 封装为拦截器
【1月更文挑战第1天】本文分析了 Advice 被封装成 MethodInterceptor 的过程,Spring AOP 用到的五种 Advice 中,有些本身就是 MethodInterceptor 的实现类,而有些需要通过适配器的封装。
44 0
|
2月前
|
Java 测试技术 数据库连接
【Spring源码解读!底层原理高级进阶】【下】探寻Spring内部:BeanFactory和ApplicationContext实现原理揭秘✨
【Spring源码解读!底层原理高级进阶】【下】探寻Spring内部:BeanFactory和ApplicationContext实现原理揭秘✨
|
4月前
|
Java Spring
Spring 源码阅读 71:基于 CGLIB 的 AOP 代理的原理(1)- DynamicAdvisedInterceptor 分析
【1月更文挑战第6天】本文分析了基于 CGLIB 的 AOP 代理对象,是通过一个 DynamicAdvisedInterceptor 类型的 Callback 来完成 AOP 增强逻辑处理的,DynamicAdvisedInterceptor 通过实现 MethodInterceptor 接口的intercept方法来处理 AOP 增强逻辑。下一篇,将重点分析这个方法的原理。
56 7
|
2月前
|
XML Java 开发者
【Spring源码解读 底层原理高级进阶】【上】探寻Spring内部:BeanFactory和ApplicationContext实现原理讲解
【Spring源码解读 底层原理高级进阶】【上】探寻Spring内部:BeanFactory和ApplicationContext实现原理讲解
|
4月前
|
XML Java 数据格式
Spring5源码(26)-ApplicationContext容器refresh过程简析
Spring5源码(26)-ApplicationContext容器refresh过程简析
39 0
|
4月前
|
XML Java 开发者
Spring 源码的阅读心得
【1月更文挑战第12天】最近花了很多时间去阅读Spring框架核心部分的源码,本文将分享一些阅读的思路和心得,分享给想阅读源码但是不知道如何下手或者读不下来的小伙伴。
64 1
|
4月前
|
缓存 Java Spring
Spring 源码阅读 75:@EnableAsync 分析
【1月更文挑战第10天】本文以 @EnableAsync 作为切入点,分析了 Spring 开启基于注解的异步任务特性的原理。
37 0
|
4月前
|
缓存 Java 数据库连接
Spring 源码阅读 74:BeanFactoryTransactionAttributeSourceAdvisor 分析
【1月更文挑战第9天】本文通过对 BeanFactoryTransactionAttributeSourceAdvisor 类的分析,了解了 Spring 是如何通过 AOP 来完成事务的管理的,本文的内容需要你对 Spring 的 AOP 的实现原理有一定的了解。
49 0