ApplicationContext体系汇总
ApplicationContext整体可以分为两个体系,一个就是web体系,另外一个就是非web体系。
非web体系
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体系
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,任重而道远,加油吧!~