From efd2091ba112855e85f07ed94ec7fc0de385a9fd Mon Sep 17 00:00:00 2001 From: senntyou Date: Tue, 12 Oct 2021 15:29:52 +0800 Subject: [PATCH] add spring/2 --- spring/2.md | 977 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 977 insertions(+) create mode 100644 spring/2.md diff --git a/spring/2.md b/spring/2.md new file mode 100644 index 0000000..1d8dea6 --- /dev/null +++ b/spring/2.md @@ -0,0 +1,977 @@ +# Spring 源码解析二:SpringMVC 的上下文组件(WebApplicationContext) + +上一篇分析 `DispatcherServlet` 与 `ContextLoaderListener` 这两个类,分析了应用初始化与请求处理的流程,但还有一些组件需要分析: + +- `ConfigurableWebApplicationContext.refresh` 刷新上下文 +- `ApplicationContext.getBean` 从上下文中获取 bean +- `DispatcherServlet.properties` 文件中定义的策略处理 +- `ContextLoader.properties` 文件中定义的策略处理 +- `View.render` 视图渲染 + +这一章来看看 `ContextLoader.properties` 文件中定义的策略处理 + +`ContextLoader.properties` 文件中只定义了一个策略 + +```properties +org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext +``` + +默认使用 `XmlWebApplicationContext`(基于 XML 加载)作为应用上下文 + +`spring-web` 内部定义 5 个应用上下文类: + +- [GenericWebApplicationContext](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-web/src/main/java/org/springframework/web/context/support/GenericWebApplicationContext.java):`WebApplicationContext`的基础实现,但不能通过配置文件和注解加载应用配置与 bean,很少用到 +- [StaticWebApplicationContext](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-web/src/main/java/org/springframework/web/context/support/StaticWebApplicationContext.java):也是`WebApplicationContext`的基础实现,但不支持 i18n,主要用于测试,不用产品环境 +- [XmlWebApplicationContext](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-web/src/main/java/org/springframework/web/context/support/XmlWebApplicationContext.java):基于 XML 加载应用配置与 bean 的`WebApplicationContext`实现,是 SpringMVC 的默认 Context +- [AnnotationConfigWebApplicationContext](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-web/src/main/java/org/springframework/web/context/support/AnnotationConfigWebApplicationContext.java):基于注解如 `@Configuration, @bean` 等加载应用配置与 bean 的`WebApplicationContext`实现,是 SpringBoot 的基础 +- [GroovyWebApplicationContext](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-web/src/main/java/org/springframework/web/context/support/GroovyWebApplicationContext.java):与`XmlWebApplicationContext`的实现差不多,但可以用 Groovy 代替 xml 做配置文件,目前用得不多 + +先来看看这 5 个应用上下文类各自的继承关系 + +``` +- ResourceLoader + - DefaultResourceLoader + - AbstractApplicationContext + - GenericApplicationContext + - GenericWebApplicationContext + +- ResourceLoader + - DefaultResourceLoader + - AbstractApplicationContext + - GenericApplicationContext + - StaticApplicationContext + - StaticWebApplicationContext + +- ResourceLoader + - DefaultResourceLoader + - AbstractApplicationContext + - AbstractRefreshableApplicationContext + - AbstractRefreshableConfigApplicationContext + - AbstractRefreshableWebApplicationContext + - XmlWebApplicationContext + +- ResourceLoader + - DefaultResourceLoader + - AbstractApplicationContext + - AbstractRefreshableApplicationContext + - AbstractRefreshableConfigApplicationContext + - AbstractRefreshableWebApplicationContext + - AnnotationConfigWebApplicationContext + +- ResourceLoader + - DefaultResourceLoader + - AbstractApplicationContext + - AbstractRefreshableApplicationContext + - AbstractRefreshableConfigApplicationContext + - AbstractRefreshableWebApplicationContext + - GroovyWebApplicationContext +``` + +我们可以发现每个类都继承 `AbstractApplicationContext`,而 `XmlWebApplicationContext, AnnotationConfigWebApplicationContext, GroovyWebApplicationContext` 都继承 `AbstractRefreshableWebApplicationContext` + +## 1. ResourceLoader + +[ResourceLoader](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-core/src/main/java/org/springframework/core/io/ResourceLoader.java) + +```java +public interface ResourceLoader { + // 根据一个字符位置信息获取资源 + Resource getResource(String location); + + // 获取资源加载器 + ClassLoader getClassLoader(); +} +``` + +## 2. DefaultResourceLoader + +[DefaultResourceLoader](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-core/src/main/java/org/springframework/core/io/DefaultResourceLoader.java) + +```java +public class DefaultResourceLoader implements ResourceLoader { + @Override + public ClassLoader getClassLoader() { + // 如果有指定的classLoader,则返回指定的,没有则返回默认的 + return (this.classLoader != null ? this.classLoader : ClassUtils.getDefaultClassLoader()); + } + + @Override + public Resource getResource(String location) { + // 自定义协议解析 + for (ProtocolResolver protocolResolver : getProtocolResolvers()) { + Resource resource = protocolResolver.resolve(location, this); + if (resource != null) { + return resource; + } + } + + // 如果以/开头,则认为是classpath资源 + if (location.startsWith("/")) { + return getResourceByPath(location); + } + // 如果以classpath:开头的classpath资源 + else if (location.startsWith(CLASSPATH_URL_PREFIX)) { + return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader()); + } + else { + try { + // 尝试以文件或url对待 + URL url = new URL(location); + return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url)); + } + catch (MalformedURLException ex) { + // 失败则默认是classpath资源 + return getResourceByPath(location); + } + } + } +} +``` + +## 3. AbstractApplicationContext + +[AbstractApplicationContext](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java) + +```java +public abstract class AbstractApplicationContext extends DefaultResourceLoader + implements ConfigurableApplicationContext {} +``` + +而 [ConfigurableApplicationContext](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-context/src/main/java/org/springframework/context/ConfigurableApplicationContext.java) 及其继承的接口主要定义以下的接口 + +```java +public interface ConfigurableApplicationContext { + // 获取bean + Object getBean(String name) throws BeansException; + T getBean(String name, Class requiredType) throws BeansException; + Object getBean(String name, Object... args) throws BeansException; + T getBean(Class requiredType) throws BeansException; + T getBean(Class requiredType, Object... args) throws BeansException; + + // 通过类型或注解获取bean + Map getBeansOfType(@Nullable Class type) throws BeansException; + Map getBeansOfType(@Nullable Class type, boolean includeNonSingletons, boolean allowEagerInit) + throws BeansException; + Map getBeansWithAnnotation(Class annotationType) throws BeansException; + + // 获取环境 + ConfigurableEnvironment getEnvironment(); + + // 刷新上下文数据 + void refresh() throws BeansException, IllegalStateException; + + // 根据locationPattern获取多个资源,如通配符* + Resource[] getResources(String locationPattern) throws IOException; +} +``` + +### 3.1. AbstractApplicationContext.getEnvironment + +[AbstractApplicationContext.getEnvironment](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java#L339) + +```java +public abstract class AbstractApplicationContext extends DefaultResourceLoader + implements ConfigurableApplicationContext { + @Override + public ConfigurableEnvironment getEnvironment() { + if (this.environment == null) { + this.environment = createEnvironment(); + } + return this.environment; + } + + protected ConfigurableEnvironment createEnvironment() { + // 内置的标准环境(也可以通过setEnvironment方法自定义环境处理机制) + // 这里可以使用 `application-dev.yml, application-test.yml, application-prod.yml, ...` 来根据环境加载不同的配置的底层实现 + // 是spring-boot的基本功能 + return new StandardEnvironment(); + } +} +``` + +### 3.2. AbstractApplicationContext.getBean + +[AbstractApplicationContext.getBean](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java#L1157) + +```java +public abstract class AbstractApplicationContext extends DefaultResourceLoader + implements ConfigurableApplicationContext { + @Override + public Object getBean(String name) throws BeansException { + return getBeanFactory().getBean(name); + } + + @Override + public T getBean(String name, Class requiredType) throws BeansException { + return getBeanFactory().getBean(name, requiredType); + } + + @Override + public Object getBean(String name, Object... args) throws BeansException { + return getBeanFactory().getBean(name, args); + } + + @Override + public T getBean(Class requiredType) throws BeansException { + return getBeanFactory().getBean(requiredType); + } + + @Override + public T getBean(Class requiredType, Object... args) throws BeansException { + return getBeanFactory().getBean(requiredType, args); + } + + // 留给子类实现 + public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException; +} +``` + +因为不同的`Context`注册 bean 的方式不一样,所以`getBeanFactory`留给子类来实现 + +### 3.3. AbstractApplicationContext.getBeansOfType + +[AbstractApplicationContext.getBeansOfType](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java#L1303) + +```java +public abstract class AbstractApplicationContext extends DefaultResourceLoader + implements ConfigurableApplicationContext { + @Override + public Map getBeansOfType(@Nullable Class type) throws BeansException { + return getBeanFactory().getBeansOfType(type); + } + + @Override + public Map getBeansOfType(@Nullable Class type, boolean includeNonSingletons, boolean allowEagerInit) + throws BeansException { + return getBeanFactory().getBeansOfType(type, includeNonSingletons, allowEagerInit); + } + + @Override + public Map getBeansWithAnnotation(Class annotationType) + throws BeansException { + return getBeanFactory().getBeansWithAnnotation(annotationType); + } + + // 留给子类实现 + public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException; +} +``` + +### 3.4. AbstractApplicationContext.refresh + +[AbstractApplicationContext.refresh](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java#L550) + +```java +public abstract class AbstractApplicationContext extends DefaultResourceLoader + implements ConfigurableApplicationContext { + @Override + public void refresh() throws BeansException, IllegalStateException { + synchronized (this.startupShutdownMonitor) { + // ... 代码省略 + + // 初始化事件容器与监听器,检查必须的属性配置,并载入必要的实例 + prepareRefresh(); + + // 刷新上下文的bean,获取bean工厂 + ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); + + // 预备bean工厂 + prepareBeanFactory(beanFactory); + + try { + // 后置处理bean工厂 + postProcessBeanFactory(beanFactory); + + // ... 代码省略 + + // 调用bean工厂的后置处理器,以使在所有bean实例化之前,可以自定义添加自己的BeanPostProcessor(bean实例化后置操作) + invokeBeanFactoryPostProcessors(beanFactory); + + // 给bean工厂注册BeanPostProcessor(bean实例化后置操作) + registerBeanPostProcessors(beanFactory); + + // ... 代码省略 + + // 实例化applicationEventMulticaster bean,作为应用事件广播器 + initApplicationEventMulticaster(); + + // 扩展实现,留给开发者,默认不实现 + onRefresh(); + + // 注册应用事件监听器 + registerListeners(); + + // 初始化所有单例的bean + finishBeanFactoryInitialization(beanFactory); + + // 刷新上下文数据完成,做一些后续处理 + finishRefresh(); + } + + catch (BeansException ex) { + // ... 代码省略 + } + + finally { + // ... 代码省略 + } + } + } + + // 刷新上下文的bean,获取bean工厂 + protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { + // 刷新上下文的bean + refreshBeanFactory(); + // 获取bean工厂 + return getBeanFactory(); + } + + // 刷新上下文的bean,由子类实现 + protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException; + + // 获取bean工厂,由子类实现 + public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException; + + // 扩展实现,留给开发者,默认不实现 + protected void onRefresh() throws BeansException {} + + // 初始化所有单例的bean + protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { + // ... 代码省略 + + // 固化所有bean的配置,后面不再更改 + beanFactory.freezeConfiguration(); + + // 初始化所有单例的bean + beanFactory.preInstantiateSingletons(); + } + + // 刷新上下文数据完成,做一些后续处理 + protected void finishRefresh() { + // 清除一些资源缓存 + clearResourceCaches(); + + // 实例化lifecycleProcessor bean + initLifecycleProcessor(); + + // 实例化Lifecycle bean,并调用这些bean的start方法 + getLifecycleProcessor().onRefresh(); + + // 派发事件 + publishEvent(new ContextRefreshedEvent(this)); + + // ... 代码省略 + } +} +``` + +- 因为不同的`Context`注册 bean 的方式不一样,所以`refreshBeanFactory, postProcessBeanFactory`留给子类来实现 +- `ConfigurableListableBeanFactory`如何加载、实例化 bean,后面再解析 + +### 3.5. AbstractApplicationContext.prepareBeanFactory + +[AbstractApplicationContext.prepareBeanFactory](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java#L685) + +```java +public abstract class AbstractApplicationContext extends DefaultResourceLoader + implements ConfigurableApplicationContext { + protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) { + // ... 代码省略 + + // 添加对 #{} SpEL Spring 表达式语言的支持 + if (!shouldIgnoreSpel) { + beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader())); + } + + // 添加属性编辑器,xml、yaml 中定义的值转换成对象就是依赖这里实现的 + beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment())); + + // Configure the bean factory with context callbacks. + beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this)); + + // ... 代码省略 + + // 注册几个可以autowirable自动载入的实例 + beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory); + beanFactory.registerResolvableDependency(ResourceLoader.class, this); + beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this); + beanFactory.registerResolvableDependency(ApplicationContext.class, this); + + // ... 代码省略 + + // 注册几个单例bean + if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) { + beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment()); + } + if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) { + beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties()); + } + if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) { + beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment()); + } + if (!beanFactory.containsLocalBean(APPLICATION_STARTUP_BEAN_NAME)) { + beanFactory.registerSingleton(APPLICATION_STARTUP_BEAN_NAME, getApplicationStartup()); + } + } +} +``` + +- `ResourceEditorRegistrar`如何注册属性编辑器、属性编辑器如何解析为对象,后面再解析 + +### 3.6. AbstractApplicationContext.getResources + +[AbstractApplicationContext.getResources](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java#L1415) + +```java +public abstract class AbstractApplicationContext extends DefaultResourceLoader + implements ConfigurableApplicationContext { + private ResourcePatternResolver resourcePatternResolver; + + public AbstractApplicationContext() { + this.resourcePatternResolver = getResourcePatternResolver(); + } + + protected ResourcePatternResolver getResourcePatternResolver() { + return new PathMatchingResourcePatternResolver(this); + } + + @Override + public Resource[] getResources(String locationPattern) throws IOException { + return this.resourcePatternResolver.getResources(locationPattern); + } +} +``` + +- `PathMatchingResourcePatternResolver`如何解析、加载 locationPattern 指定的资源,后面再解析 + +### 3.7. 综述 + +总的来说,`AbstractApplicationContext` 类完成上下文环境的大部分功能,包括环境加载、bean 的加载与前置后置处理、事件派发、完成一些初始化工作等, +但扩展了几个接口给子类实现,如如何加载、注册、实例化 bean 等 + +## 4. GenericApplicationContext + +[GenericApplicationContext](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-context/src/main/java/org/springframework/context/support/GenericApplicationContext.java) + +```java +public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {} +``` + +[BeanDefinitionRegistry](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionRegistry.java) 这个接口主要定义了注册 bean 的定义及别名 + +```java +public interface BeanDefinitionRegistry { + // 注册bean定义 + void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) + throws BeanDefinitionStoreException; + + // 删除bean定义 + void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException; + + // 获取bean定义 + BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException; + + // 检查bean定义 + boolean containsBeanDefinition(String beanName); + + // 注册bean别名 + void registerAlias(String name, String alias); + + // 删除bean别名 + void removeAlias(String alias); + + // 检查bean别名 + boolean isAlias(String name); + + // 获取bean别名 + String[] getAliases(String name); +} +``` + +来看看 `GenericApplicationContext` 如何实现这些接口的 + +```java +public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry { + @Override + public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) + throws BeanDefinitionStoreException { + + this.beanFactory.registerBeanDefinition(beanName, beanDefinition); + } + + @Override + public void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException { + this.beanFactory.removeBeanDefinition(beanName); + } + + @Override + public BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException { + return this.beanFactory.getBeanDefinition(beanName); + } + + @Override + public void registerAlias(String beanName, String alias) { + this.beanFactory.registerAlias(beanName, alias); + } + + @Override + public void removeAlias(String alias) { + this.beanFactory.removeAlias(alias); + } + + @Override + public boolean isAlias(String beanName) { + return this.beanFactory.isAlias(beanName); + } +} +``` + +最终还是落脚在 `beanFactory` 上 + +## 5. GenericWebApplicationContext + +[GenericWebApplicationContext](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-web/src/main/java/org/springframework/web/context/support/GenericWebApplicationContext.java) + +```java +public class GenericWebApplicationContext extends GenericApplicationContext + implements ConfigurableWebApplicationContext, ThemeSource {} +``` + +[ConfigurableWebApplicationContext](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-web/src/main/java/org/springframework/web/context/ConfigurableWebApplicationContext.java) + +```java +public interface ConfigurableWebApplicationContext { + // 设置bean配置文件来源 + void setConfigLocation(String configLocation); + // 设置多个bean配置文件来源 + void setConfigLocations(String... configLocations); + // 获取bean配置文件来源 + String[] getConfigLocations(); +} +``` + +`ConfigurableWebApplicationContext`扩展了`WebApplicationContext`,定义了允许通过配置的方式实例化上下文环境 + +```java +public class GenericWebApplicationContext extends GenericApplicationContext + implements ConfigurableWebApplicationContext, ThemeSource { + @Override + protected ConfigurableEnvironment createEnvironment() { + // StandardServletEnvironment扩展了StandardEnvironment + // 增加了可以从Servlet context init parameters和Servlet config init parameters增加应用配置来源 + return new StandardServletEnvironment(); + } + + // 不可设置bean配置文件来源 + @Override + public void setConfigLocation(String configLocation) { + if (StringUtils.hasText(configLocation)) { + throw new UnsupportedOperationException( + "GenericWebApplicationContext does not support setConfigLocation(). " + + "Do you still have an 'contextConfigLocations' init-param set?"); + } + } + + @Override + public void setConfigLocations(String... configLocations) { + if (!ObjectUtils.isEmpty(configLocations)) { + throw new UnsupportedOperationException( + "GenericWebApplicationContext does not support setConfigLocations(). " + + "Do you still have an 'contextConfigLocations' init-param set?"); + } + } + + @Override + public String[] getConfigLocations() { + throw new UnsupportedOperationException( + "GenericWebApplicationContext does not support getConfigLocations()"); + } +} +``` + +`GenericWebApplicationContext` 并未实现 `ConfigurableWebApplicationContext` 的核心方法,也就不能通过文件加载配置,该类设计的目的不是在`web.xml`中进行声明式的安装,而是编程式的安装,例如使用`WebApplicationInitializers`来构建内嵌的上下文;一般很少用到 + +## 6. StaticWebApplicationContext + +因为 [StaticApplicationContext](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-context/src/main/java/org/springframework/context/support/StaticApplicationContext.java) 实现功能比较少,放在这里一起解析 + +```java +public class StaticApplicationContext extends GenericApplicationContext { + private final StaticMessageSource staticMessageSource; + + public StaticApplicationContext(@Nullable ApplicationContext parent) throws BeansException { + super(parent); + + // 上下文对象中有一个messageSource组件,实现了i18n功能 + // 而StaticMessageSource实现的是由程序载入文本,而非文件,便是去掉了i18n功能 + this.staticMessageSource = new StaticMessageSource(); + getBeanFactory().registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.staticMessageSource); + } +} +``` + +[StaticWebApplicationContext](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-web/src/main/java/org/springframework/web/context/support/StaticWebApplicationContext.java) 实现功能也比较少 + +```java +public class StaticWebApplicationContext extends StaticApplicationContext + implements ConfigurableWebApplicationContext, ThemeSource { + // 不可设置bean配置文件来源 + @Override + public void setConfigLocation(String configLocation) { + throw new UnsupportedOperationException("StaticWebApplicationContext does not support config locations"); + } + + @Override + public void setConfigLocations(String... configLocations) { + throw new UnsupportedOperationException("StaticWebApplicationContext does not support config locations"); + } + + @Override + public String[] getConfigLocations() { + return null; + } +} +``` + +`StaticWebApplicationContext` 也并未实现 `ConfigurableWebApplicationContext` 的核心方法,也就不能通过文件加载配置,该类设计的目的主要用于测试,不用于产品环境 + +## 7. AbstractRefreshableApplicationContext + +因为 [AbstractRefreshableApplicationContext](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-context/src/main/java/org/springframework/context/support/AbstractRefreshableApplicationContext.java) + +```java +public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext { + @Override + protected final void refreshBeanFactory() throws BeansException { + // ... 代码省略 + try { + // 创建bean工厂 + DefaultListableBeanFactory beanFactory = createBeanFactory(); + + // ... 代码省略 + + // 加载bean的定义 + loadBeanDefinitions(beanFactory); + this.beanFactory = beanFactory; + } + catch (IOException ex) { + throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); + } + } + + // 创建bean工厂 + protected DefaultListableBeanFactory createBeanFactory() { + // 默认使用DefaultListableBeanFactory创建bean工厂 + return new DefaultListableBeanFactory(getInternalParentBeanFactory()); + } + + // 加载bean的定义,由子类实现 + protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) + throws BeansException, IOException; +} +``` + +## 8. AbstractRefreshableConfigApplicationContext + +因为 [AbstractRefreshableConfigApplicationContext](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-context/src/main/java/org/springframework/context/support/AbstractRefreshableConfigApplicationContext.java) + +```java +public abstract class AbstractRefreshableConfigApplicationContext extends AbstractRefreshableApplicationContext + implements BeanNameAware, InitializingBean { + // 设置配置文件来源,以",; \t\n"分隔多个 + public void setConfigLocation(String location) { + setConfigLocations(StringUtils.tokenizeToStringArray(location, CONFIG_LOCATION_DELIMITERS)); + } + + public void setConfigLocations(@Nullable String... locations) { + if (locations != null) { + this.configLocations = new String[locations.length]; + for (int i = 0; i < locations.length; i++) { + // 解析路径,替换${}占位符 + this.configLocations[i] = resolvePath(locations[i]).trim(); + } + } + else { + this.configLocations = null; + } + } + + // 获取配置文件来源集,如果没有,则返回默认的 + protected String[] getConfigLocations() { + return (this.configLocations != null ? this.configLocations : getDefaultConfigLocations()); + } + + // 默认的配置文件来源集由子类实现 + protected String[] getDefaultConfigLocations() { + return null; + } + + // 解析路径,替换${}占位符,有PropertySourcesPropertyResolver.resolveRequiredPlaceholders实现此功能 + protected String resolvePath(String path) { + return getEnvironment().resolveRequiredPlaceholders(path); + } +} +``` + +- `AbstractRefreshableConfigApplicationContext` 实现了 `ConfigurableWebApplicationContext` 的核心方法,也就是可以文件加载配置 +- `PropertySourcesPropertyResolver`如何是解析路径的,后面再解析 + +## 9. XmlWebApplicationContext + +因为 [AbstractRefreshableWebApplicationContext](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-web/src/main/java/org/springframework/web/context/support/AbstractRefreshableWebApplicationContext.java) 实现功能比较少,放在这里一起解析 + +```java +public abstract class AbstractRefreshableWebApplicationContext extends AbstractRefreshableConfigApplicationContext + implements ConfigurableWebApplicationContext, ThemeSource { + @Override + protected ConfigurableEnvironment createEnvironment() { + // StandardServletEnvironment扩展了StandardEnvironment + // 增加了可以从Servlet context init parameters和Servlet config init parameters增加应用配置来源 + return new StandardServletEnvironment(); + } +} +``` + +因为 [XmlWebApplicationContext](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-web/src/main/java/org/springframework/web/context/support/XmlWebApplicationContext.java) 实现功能比较少,放在这里一起解析 + +```java +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 void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { + // 创建一个bean定义的xml解析器,用XmlBeanDefinitionReader实现 + XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); + + // ... 代码省略 + + // 载入bean定义 + loadBeanDefinitions(beanDefinitionReader); + } + + protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException { + String[] configLocations = getConfigLocations(); + if (configLocations != null) { + for (String configLocation : configLocations) { + // 通过配置文件载入bean定义 + reader.loadBeanDefinitions(configLocation); + } + } + } + + @Override + protected String[] getDefaultConfigLocations() { + if (getNamespace() != null) { + // 如果有servlet-name(如testapp),用前缀后缀包裹为"/WEB-INF/testapp-servlet.xml" + return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX}; + } + else { + // 如果没有,默认为"/WEB-INF/applicationContext.xml"文件 + return new String[] {DEFAULT_CONFIG_LOCATION}; + } + } +} +``` + +`XmlWebApplicationContext` 主要解决了 2 个问题: + +1. 定义了默认的配置文件,有 servlet-name(如`testapp`),用前缀后缀包裹为`/WEB-INF/testapp-servlet.xml`,如果没有 servlet-name,则为`/WEB-INF/applicationContext.xml` +1. 创建一个 bean 定义的 xml 解析器,并通过配置文件载入 bean 定义 + +SpringMVC 框架的默认加载机制便是使用`XmlWebApplicationContext`作为上下文环境,从 `xml` 文件加载配置与 bean 定义 + +至于`XmlBeanDefinitionReader`如何是解析 bean 定义的,后面再解析 + +## 10. AnnotationConfigWebApplicationContext + +[AnnotationConfigWebApplicationContext](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-web/src/main/java/org/springframework/web/context/support/AnnotationConfigWebApplicationContext.java) + +```java +public class AnnotationConfigWebApplicationContext extends AbstractRefreshableWebApplicationContext + implements AnnotationConfigRegistry {} +``` + +先来看看 [AnnotationConfigRegistry](<[AnnotationConfigWebApplicationContext](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigRegistry.java)>) + +```java +public interface AnnotationConfigRegistry { + // 根据类名注册组件 + void register(Class... componentClasses); + + // 根据包名扫描组件 + void scan(String... basePackages); +} +``` + +这两个方法正好是通过注解如 `@Configuration, @bean, @Component, @Controller, @Service` 等注册 bean 的底层机制 + +来看看 `AnnotationConfigWebApplicationContext` 是如何实现的 + +```java +public class AnnotationConfigWebApplicationContext extends AbstractRefreshableWebApplicationContext + implements AnnotationConfigRegistry { + // 组件类集合 + private final Set> componentClasses = new LinkedHashSet<>(); + // 扫描包名集合 + private final Set basePackages = new LinkedHashSet<>(); + + // 注册组件 + @Override + public void register(Class... componentClasses) { + Collections.addAll(this.componentClasses, componentClasses); + } + + // 添加扫描包名 + @Override + public void scan(String... basePackages) { + Collections.addAll(this.basePackages, basePackages); + } + + // 加载bean定义 + @Override + protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) { + // 创建一个bean定义的注解解析器,用AnnotatedBeanDefinitionReader实现 + AnnotatedBeanDefinitionReader reader = getAnnotatedBeanDefinitionReader(beanFactory); + // 创建一个基于包名的bean注解扫描器,用ClassPathBeanDefinitionScanner实现 + ClassPathBeanDefinitionScanner scanner = getClassPathBeanDefinitionScanner(beanFactory); + + // 创建一个bean命名生成器 + BeanNameGenerator beanNameGenerator = getBeanNameGenerator(); + if (beanNameGenerator != null) { + reader.setBeanNameGenerator(beanNameGenerator); + scanner.setBeanNameGenerator(beanNameGenerator); + beanFactory.registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, beanNameGenerator); + } + + // 创建一个bean作用域元信息解析器,判断注册的bean是原生类型(prototype)还是单例类型(singleton) + ScopeMetadataResolver scopeMetadataResolver = getScopeMetadataResolver(); + if (scopeMetadataResolver != null) { + reader.setScopeMetadataResolver(scopeMetadataResolver); + scanner.setScopeMetadataResolver(scopeMetadataResolver); + } + + // 注册组件类 + if (!this.componentClasses.isEmpty()) { + reader.register(ClassUtils.toClassArray(this.componentClasses)); + } + + // 扫描包 + 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()); + reader.register(clazz); + } + catch (ClassNotFoundException ex) { + int count = scanner.scan(configLocation); + // ... 代码省略 + } + } + } + } + + // 创建一个bean定义的注解解析器,用AnnotatedBeanDefinitionReader实现 + protected AnnotatedBeanDefinitionReader getAnnotatedBeanDefinitionReader(DefaultListableBeanFactory beanFactory) { + return new AnnotatedBeanDefinitionReader(beanFactory, getEnvironment()); + } + + // 创建一个基于包名的bean注解扫描器,用ClassPathBeanDefinitionScanner实现 + protected ClassPathBeanDefinitionScanner getClassPathBeanDefinitionScanner(DefaultListableBeanFactory beanFactory) { + return new ClassPathBeanDefinitionScanner(beanFactory, true, getEnvironment()); + } +} +``` + +实际上,注册 bean 是由`AnnotatedBeanDefinitionReader`完成,扫描包是由`ClassPathBeanDefinitionScanner`完成,这两个类后面再解析 + +## 11. GroovyWebApplicationContext + +[GroovyWebApplicationContext](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-web/src/main/java/org/springframework/web/context/support/GroovyWebApplicationContext.java) + +```java +public class GroovyWebApplicationContext extends AbstractRefreshableWebApplicationContext implements GroovyObject { + // 默认配置文件 + public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.groovy"; + + // 默认配置文件前缀 + public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/"; + + // 默认配置文件后缀 + public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".groovy"; + + @Override + protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { + // 创建一个bean定义的Groovy解析器,用GroovyBeanDefinitionReader实现 + GroovyBeanDefinitionReader beanDefinitionReader = new GroovyBeanDefinitionReader(beanFactory); + + // ... 代码省略 + + // 载入bean定义 + loadBeanDefinitions(beanDefinitionReader); + } + + protected void loadBeanDefinitions(GroovyBeanDefinitionReader reader) throws IOException { + String[] configLocations = getConfigLocations(); + if (configLocations != null) { + for (String configLocation : configLocations) { + // 通过配置文件载入bean定义 + reader.loadBeanDefinitions(configLocation); + } + } + } + + @Override + protected String[] getDefaultConfigLocations() { + if (getNamespace() != null) { + // 如果有servlet-name(如testapp),用前缀后缀包裹为"/WEB-INF/testapp-servlet.groovy" + return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX}; + } + else { + // 如果没有,默认为"/WEB-INF/applicationContext.groovy"文件 + return new String[] {DEFAULT_CONFIG_LOCATION}; + } + } +} +``` + +`GroovyWebApplicationContext` 的运行机制与 `XmlWebApplicationContext` 差不多,从 `groovy` 文件加载配置与 bean 定义 + +## 12. 综述 + +[WebApplicationContext](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-web/src/main/java/org/springframework/web/context/WebApplicationContext.java) 定义了 Web 应用初始化的基本流程,主要有 5 个实现类,常用的是:基于 Xml 加载的`XmlWebApplicationContext`与基于注解加载的`AnnotationConfigWebApplicationContext` + +- [GenericWebApplicationContext](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-web/src/main/java/org/springframework/web/context/support/GenericWebApplicationContext.java)、[StaticWebApplicationContext](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-web/src/main/java/org/springframework/web/context/support/StaticWebApplicationContext.java):这两者都只是`WebApplicationContext`的基础实现,都不能通过配置文件和注解加载应用配置与 bean +- [XmlWebApplicationContext](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-web/src/main/java/org/springframework/web/context/support/XmlWebApplicationContext.java):基于 XML 加载应用配置与 bean 的上下文环境,是 SpringMVC 的默认 Context +- [AnnotationConfigWebApplicationContext](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-web/src/main/java/org/springframework/web/context/support/AnnotationConfigWebApplicationContext.java):基于注解如 `@Configuration, @bean` 等加载应用配置与 bean 的上下文环境,是 SpringBoot 的基础 +- [GroovyWebApplicationContext](https://github.com/spring-projects/spring-framework/blob/v5.3.10/spring-web/src/main/java/org/springframework/web/context/support/GroovyWebApplicationContext.java):与`XmlWebApplicationContext`的实现差不多,但可以用 Groovy 代替 xml 做配置文件,但目前 Groovy 远不及 Xml 普及,用的仍然不多 + +这一节仍然有一些点留待下次解析: + +- `ConfigurableListableBeanFactory`如何加载、实例化 bean +- `ResourceEditorRegistrar`如何注册属性编辑器、属性编辑器如何解析为对象 +- `PathMatchingResourcePatternResolver`如何解析、加载 locationPattern 指定的资源 +- `PropertySourcesPropertyResolver`如何是解析路径的 +- `XmlBeanDefinitionReader`如何是解析 bean 定义的 +- `AnnotatedBeanDefinitionReader`是如何注册 bean 的 +- `ClassPathBeanDefinitionScanner`是如何扫描包的 + +## 后续 + +更多博客,查看 [https://github.com/senntyou/blogs](https://github.com/senntyou/blogs) + +作者:[深予之 (@senntyou)](https://github.com/senntyou) + +版权声明:自由转载-非商用-非衍生-保持署名([创意共享 3.0 许可证](https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh))