【小家Spring】BeanFactory体系和ApplicationContext体系,两大体系各接口分析、区别和联系(下)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 【小家Spring】BeanFactory体系和ApplicationContext体系,两大体系各接口分析、区别和联系(下)

一级接口:ApplicationContext


EnvironmentCapable:可配置Environment

ListableBeanFactory:前面有介绍:可将Bean逐一列出的工厂

HierarchicalBeanFactory:前面有介绍:分层的工厂

MessageSource:可管理message实现国际化等功能

ApplicationEventPublisher:可publish事件,调用Listener

ResourcePatternResolver:加载pattern指定的资源

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
    MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
  //容器的unique id
  @Nullable
  String getId();
  // 部署的应用的名称 web应用一般会把servletContext.getContextPath()赋值给他
  // 非web应用就是""
  String getApplicationName();
  // 该应用context展示的名称
  String getDisplayName();
  // 容器首次被加载、启动的时间
  long getStartupDate();
  // 获取父容器 有可能为null
  @Nullable
  ApplicationContext getParent();
  // 上面都没什么大作用,这个方法:虽然不继承AutowireCapableBeanFactory,但是我们可通过此方法得到它,从而用它的相关功能
  AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
}


二级接口:WebApplicationContext、ConfigurableApplicationContext


这是它两个直接子接口。

WebApplicationContext:web环境的Context

顾名思义嘛。但是它扩展的功能非常非常少~ 该容器是read-only 的

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";
  // 获取到Servlet的上下文====
  @Nullable
  ServletContext getServletContext();
}


ConfigurableApplicationContext:可配置的应用上下文


ApplicationContext接口本身是 read-only 的,所以子接口 ConfigurableApplicationContext就提供了如setID()、setParent()、setEnvironment()等方法,用来配置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;
}


AbstractApplicationContext

穿插在这里,不得不提一个抽象实现类,他就是:AbstractApplicationContext。它实现了该接口的大部分功能(没管Web相关的东西,所以它只管了这个接口):


public abstract class AbstractApplicationContext extends DefaultResourceLoader
    // 此处只实现了ConfigurableApplicationContext这个接口
    implements ConfigurableApplicationContext {
  public static final String MESSAGE_SOURCE_BEAN_NAME = "messageSource";
  public static final String LIFECYCLE_PROCESSOR_BEAN_NAME = "lifecycleProcessor";
  public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
  static {
    // 这里就把ContextClosedEvent 加载进来,为了兼容WebLogic 可能的一个bug
    ContextClosedEvent.class.getName();
  }
  // 然后就是很多字段,保存了一些设置信息。比如:等等
  @Nullable
  private LifecycleProcessor lifecycleProcessor;
  @Nullable
  private ApplicationEventMulticaster applicationEventMulticaster;
  private final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();
  // 这里最重要的一个实现,非refresh莫属:
  @Override
  public void refresh() throws BeansException, IllegalStateException {
    ...
  }
  //它提供下面抽象方法,由子类去实现
  //Subclasses must implement this method to perform the actual configuration load
  // 子类必须实现这个方法:子类可以创建一个新的BeanFactory,或者直接指向自己一个已经hold的引用(GenericApplicationContext就是指向自己已经持有的)
  protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;
  // 销毁BeanFactory  this.beanFactory=null
  protected abstract void closeBeanFactory();
  // refreshBeanFactory这里肯定已经有工厂实例了,这里直接返回呗
  @Override
  public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
}


实现类又扩展了其他接口或者继承了其他父类,这些只是实现类为了扩展功能或者为了对实现上述接口提供便利而做的事情,对ApplicationContext接口抽象出来的功能没有影响或者没有太大帮助


特别注意(这是经常会被忽略的地方):

AbstractApplicationContext它实现了大多数方法,包括关于Bean定义的相关方法如下:


  // 这几个方法特比特殊:子类中有实现了接口AnnotationConfigRegistry的,方法签名和这个是一模一样的。所以子类虽然实现了AnnotationConfigRegistry接口,但并不需要再实现这个方法亦可
  // 使用起来需要注意的是,这里借助的是getBeanFactory():属于ListableBeanFactory这个二级接口的方法
  // 而这个二级接口说得很清楚:它去查找的时候,不会考虑层级关系。(也就是说不去父容器找)
  // 所以使用这三个方法的时候要注意。若你确定Bean已经加载进来了,不妨也把父容器也考虑一把
  @Override
  public boolean containsBeanDefinition(String beanName) {
    return getBeanFactory().containsBeanDefinition(beanName);
  }
  @Override
  public int getBeanDefinitionCount() {
    return getBeanFactory().getBeanDefinitionCount();
  }
  @Override
  public String[] getBeanDefinitionNames() {
    return getBeanFactory().getBeanDefinitionNames();
  }
// 后面的getBeanNamesForType之类的,这里就不说了~

AbstractRefreshableApplicationContext和GenericApplicationContext


它俩是AbstractApplicationContext直接子类。


GenericApplicationContext 通用的应用上下文(请注意:它已经不是抽象类,可以直接使用了)


GenericApplicationContext 持有一个DefaultListableBeanFactory实例,并且没有假设一个特定的bean definition 的format。实现了BeanDefinitionRegistry接口以允许配置任何bean definition reader(也可以不是XmlBeanDefinitionReader)。 Generic:表现出它的通用


public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
  private final DefaultListableBeanFactory beanFactory;
  @Nullable
  private ResourceLoader resourceLoader;
  // 保证只会被刷新一次
  private final AtomicBoolean refreshed = new AtomicBoolean();
  ...
  //和其他在每次refresh时都创建一个新的内部BeanFactory实例的ApplicationContext实例不同
  //本类中的BeanFactory从一开始就创建好并可在其中注册bean definitions,refresh方法可能在其中只调用一次(并不会每次刷新的时候都会去调用的)
}


我们使用它的时候,可以先调用registerBeanDefinition,注册一些Bean定义(至于这些Bean定义从哪儿来,子类自己思考,就不一定得从xml文件来了),然后再refresh()即可,使用案例:


GenericApplicationContext ctx = new GenericApplicationContext();
//使用XmlBeanDefinitionReader
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");


当然它也有很大的缺点:它几乎是手动档的。什么解析Bean之类的,都得自己手动调用对应的Reader去处理,在当下约定大于配置的今天,直接使用它手动去控制,现在效率太低了。


一般情况下使用ClassPathXmlApplicationContext或者FileSystemXmlApplicationContext会比这个GenericApplicationContext更方便,但是但是相应地缺少灵活性,因为只能使用特定Bean definition 格式和加载路径。


当然,也可以继承它做一些扩展,进行一些步骤的简单封装处理亦可。比如:


  • GenericXmlApplicationContext:比较重要,内部使用XmlBeanDefinitionReader去解析,根据传进来的xml路径解析然后把Bean定义信息注册好,然后自动调用refresh()方法
  • StaticApplicationContext:它能让你编程注册。而不是从配置文件中读取,一般用于测试环境(忽略)
  • GenericWebApplicationContext:对web环境的支持。重点是也实现了接口:ConfigurableWebApplicationContext配置的context
  • AnnotationConfigApplicationContext:这个非常的重要,内部使用AnnotatedBeanDefinitionReader或者ClassPathBeanDefinitionScanner去装载Bean的定义信息。然后其余的没啥了。这个我们经常使用,因为不用再需要xml文件了,使用@Configuration配置类即可,更加的方便。


下面这个AbstractRefreshableApplicationContext给出了另一种解决方案:如果你追求定制化的ApplicationContext实现,可从AbstractRefreshableApplicationContext继承来实现从不同地方去加载Bean定义信息进来。


AbstractRefreshableApplicationContext


AbstractRefreshableApplicationContext实现了父类的方法refreshBeanFactory(),执行BeanFactory的“刷新”。

public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {
  ...
  @Override
  protected final void refreshBeanFactory() throws BeansException {
    //1、如果当前上下文持有一个已经刷新过的bean工厂,需要先销毁此上下文所管理的所有的bean,再关闭bean工厂
    //2、创建一个bean工厂:org.springframework.beans.factory.support.DefaultListableBeanFactory,并且把旧工厂的属性赋值给新的(若有的话)
    //3、加载xml配置文件中的bean(或者是@Configuration的定义信息)  总之就是loadBeanDefinitions(beanFactory);
    // 此处需要注意的是:loadBeanDefinitions()是个抽象方法,因为当前的并不知道去哪加载(可能是xml,可能是Config类,可能都有。因此交给子类共同去完成这个方案即可)
    // 所以看下面的实现~~~~AbstractRefreshableConfigApplicationContext
  }
}

AbstractRefreshableConfigApplicationContext


public abstract class AbstractRefreshableConfigApplicationContext extends AbstractRefreshableApplicationContext
    implements BeanNameAware, InitializingBean {
  @Nullable
  private String[] configLocations; // 配置文件的位置(可能是xml,可能是config哦~ 目前还是不确定的  这里只是提供的抽象存储)
  ...
  //它没有什么具体的操作。
}


这里它有两个比较典型的实现类:


  • FileSystemXmlApplicationContext:taking the context definition files from the file system or from URLs
  • ClassPathXmlApplicationContext:taking the context definition files from the class path

三级接口:ConfigurableWebApplicationContext 集大成者


web应用上下文使用的接口,并没有定义太多的操作,主要和Servlet上下文及配置文件。它一次性都继承了上面的两个二级接口,可以说是他俩的结合体


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();
  void setNamespace(@Nullable String namespace);
  @Nullable
  String getNamespace();
  // Spring配置文件的地方 比如默认位置为:/WEB-INF/applicationContext.xml
  void setConfigLocation(String configLocation);
  void setConfigLocations(String... configLocations);
  @Nullable
  String[] getConfigLocations();
}

此处有个非常重要的实现:AbstractRefreshableWebApplicationContext,它相当于在父类基础在,再实现了ConfigurableWebApplicationContext从而具有web的特性。它自己是抽象类,但是具体实现有如下:


  • XmlWebApplicationContext:这个可以说是web环境下Spring的默认容器。之前讲解过了ContextLoaderListener启动的时候,如果你没有指定web容器(比如若是Tomcat注解驱动的话,就会外面创建一个AnnotationConfigWebApplicationContext传进来),那么this.context = createWebApplicationContext(servletContext);(它会去找contextConfigLocation,找到xml配置文件)就会创建一个XmlWebApplicationContext类型的容器。至于为何呢?其实默认类型在配置文件:org\springframework\web\context\ContextLoader.properties这个文件里,里面写着:


org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext


这里稍微说一下,你可以根据web.xml里配置的globalInitializerClasses 和 contextInitializerClasses 对应的class来定制成你的容器类型都ok,这个前面有说过,此处就不再鳌诉了~


它的loadBeanDefinitions()方法也是从配置的xml文件里面去解析读取


  • GroovyWebApplicationContext:略
  • AnnotationConfigWebApplicationContext:在注解驱动大行其道的今天,这个就特别的重要了。它的loadBeanDefinitions()前面源码分析的时候重点分析过,因此这里就略过吧。理解好它,更有助于理解Spring Boot自己提供的容器类型:AnnotationConfigServletWebServerApplicationContext


总结


Spring是一个优秀的框架,具有良好的结构设计和接口抽象,它的每一个接口都是其功能具体到各个模块中的高度抽象,实际使用过程中相当于把接口的各个实现类按照接口所提供的组织架构装配起来以提供完整的服务,可以说掌握了Spring的接口就相当于掌握了Spring的大部分功能

相关文章
|
9天前
|
设计模式 Java Spring
spring源码设计模式分析(五)-策略模式
spring源码设计模式分析(五)-策略模式
|
7天前
|
负载均衡 Java 网络架构
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
20 5
|
9天前
|
消息中间件 设计模式 缓存
spring源码设计模式分析(四)-观察者模式
spring源码设计模式分析(四)-观察者模式
|
9天前
|
设计模式 Java Spring
spring源码设计模式分析(六)-模板方法模式
spring源码设计模式分析(六)-模板方法模式
|
9天前
|
设计模式 Java Spring
spring源码设计模式分析(七)-委派模式
spring源码设计模式分析(七)-委派模式
|
9天前
|
设计模式 Java 数据库
spring源码设计模式分析(八)-访问者模式
spring源码设计模式分析(八)-访问者模式
|
9天前
|
设计模式 搜索推荐 Java
spring源码设计模式分析(三)
spring源码设计模式分析(三)
|
9天前
|
设计模式 Java Spring
spring源码设计模式分析-代理设计模式(二)
spring源码设计模式分析-代理设计模式(二)
|
9天前
|
XML 存储 Java
Spring-源码深入分析(二)
Spring-源码深入分析(二)
|
9天前
|
XML 设计模式 Java
Spring-源码深入分析(一)
Spring-源码深入分析(一)
下一篇
无影云桌面