Spring 原始碼解析二:上下文元件(WebApplicationContext)
上一篇解析了 DispatcherServlet
與 ContextLoaderListener
這兩個類,解析了應用初始化與請求處理的流程,但還有一些元件需要解析:
ConfigurableWebApplicationContext.refresh
重新整理上下文ApplicationContext.getBean
從上下文中獲取 beanDispatcherServlet.properties
檔案中定義的策略處理ContextLoader.properties
檔案中定義的策略處理View.render
檢視渲染
這一章來看看 ContextLoader.properties
檔案中定義的策略處理
ContextLoader.properties
檔案中只定義了一個策略
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
預設使用 XmlWebApplicationContext
(基於 XML 載入)作為應用上下文
spring-web
內部定義 5 個應用上下文類:
- GenericWebApplicationContext
:WebApplicationContext
的基礎實現,但不能通過配置檔案和註解載入應用配置與 bean,一般用於擴充套件實現(如 SpringBoot),很少直接使用 - StaticWebApplicationContext
:也是WebApplicationContext
的基礎實現,但不支援 i18n,主要用於測試,不用產品環境 - XmlWebApplicationContext
:基於 XML 載入應用配置與 bean 的WebApplicationContext
實現,是 SpringMVC 的預設 Context - AnnotationConfigWebApplicationContext
:基於註解如@Configuration, @bean
等載入應用配置與 bean 的WebApplicationContext
實現 - GroovyWebApplicationContext
:與XmlWebApplicationContext
的實現差不多,但可以用 Groovy 代替 xml 做配置檔案,目前用得不多
先來看看這 5 個應用上下文類各自的繼承關係
- DefaultResourceLoader
- AbstractApplicationContext
- GenericApplicationContext
- GenericWebApplicationContext
- DefaultResourceLoader
- AbstractApplicationContext
- GenericApplicationContext
- StaticApplicationContext
- StaticWebApplicationContext
- DefaultResourceLoader
- AbstractApplicationContext
- AbstractRefreshableApplicationContext
- AbstractRefreshableConfigApplicationContext
- AbstractRefreshableWebApplicationContext
- XmlWebApplicationContext
- DefaultResourceLoader
- AbstractApplicationContext
- AbstractRefreshableApplicationContext
- AbstractRefreshableConfigApplicationContext
- AbstractRefreshableWebApplicationContext
- AnnotationConfigWebApplicationContext
- DefaultResourceLoader
- AbstractApplicationContext
- AbstractRefreshableApplicationContext
- AbstractRefreshableConfigApplicationContext
- AbstractRefreshableWebApplicationContext
- GroovyWebApplicationContext
我們可以發現每個類都繼承 AbstractApplicationContext
,而 XmlWebApplicationContext
, AnnotationConfigWebApplicationContext
,GroovyWebApplicationContext
都繼承 AbstractRefreshableWebApplicationContext
1. DefaultResourceLoader
DefaultResourceLoader
的主要功能是實現資源載入
public class DefaultResourceLoader implements ResourceLoader {}
先來看看介面ResourceLoader
public interface ResourceLoader {
// 根據一個字元位置資訊獲取資源
Resource getResource(String location);
// 獲取資源載入器
ClassLoader getClassLoader();
}
DefaultResourceLoader
是 ResourceLoader
的預設實現
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);
}
}
}
}
2. AbstractApplicationContext
AbstractApplicationContext
的主要功能是通過名字、型別或註解獲取 bean 例項,獲取上下文的環境物件與資源、重新整理上下文資料
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {}
介面 ConfigurableApplicationContext
及其繼承的介面主要定義以下的方法
public interface ConfigurableApplicationContext {
// 獲取bean
Object getBean(String name) throws BeansException;
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
// 通過型別或註解獲取bean
<T> Map<String, T> getBeansOfType(@Nullable Class<T> type) throws BeansException;
<T> Map<String, T> getBeansOfType(@Nullable Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)
throws BeansException;
Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) throws BeansException;
// 獲取環境
ConfigurableEnvironment getEnvironment();
// 重新整理上下文資料
void refresh() throws BeansException, IllegalStateException;
// 根據locationPattern獲取多個資源,如萬用字元*
Resource[] getResources(String locationPattern) throws IOException;
}
2.1. AbstractApplicationContext.getEnvironment
AbstractApplicationContext.getEnvironment
獲取環境
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();
}
}
2.2. AbstractApplicationContext.getBean
AbstractApplicationContext.getBean
獲取 bean
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
@Override
public Object getBean(String name) throws BeansException {
return getBeanFactory().getBean(name);
}
@Override
public <T> T getBean(String name, Class<T> 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> T getBean(Class<T> requiredType) throws BeansException {
return getBeanFactory().getBean(requiredType);
}
@Override
public <T> T getBean(Class<T> requiredType, Object... args) throws BeansException {
return getBeanFactory().getBean(requiredType, args);
}
// 留給子類實現
public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
}
因為不同的Context
註冊 bean 的方式不一樣,所以getBeanFactory
留給子類來實現
2.3. AbstractApplicationContext.getBeansOfType
AbstractApplicationContext.getBeansOfType
通過型別或註解獲取 bean
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
@Override
public <T> Map<String, T> getBeansOfType(@Nullable Class<T> type) throws BeansException {
return getBeanFactory().getBeansOfType(type);
}
@Override
public <T> Map<String, T> getBeansOfType(@Nullable Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)
throws BeansException {
return getBeanFactory().getBeansOfType(type, includeNonSingletons, allowEagerInit);
}
@Override
public Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType)
throws BeansException {
return getBeanFactory().getBeansWithAnnotation(annotationType);
}
// 留給子類實現
public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
}
2.4. AbstractApplicationContext.refresh
AbstractApplicationContext.refresh
重新整理上下文資料
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,後面再解析
2.5. AbstractApplicationContext.prepareBeanFactory
AbstractApplicationContext.prepareBeanFactory
預備 bean 工廠
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()));
// 新增一個BeanPostProcessor,後置處理器
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
如何註冊屬性編輯器、屬性編輯器如何解析為物件,後面再解析
2.6. AbstractApplicationContext.getResources
AbstractApplicationContext.getResources
根據 locationPattern 獲取多個資源,如萬用字元*
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 指定的資源,後面再解析
2.7. 綜述
總的來說,AbstractApplicationContext
類完成上下文環境的大部分功能,包括環境載入、bean 的載入與前置後置處理、事件派發、完成一些初始化工作等,
但擴充套件了幾個介面給子類實現,如如何載入、註冊、例項化 bean 等
3. GenericApplicationContext
GenericApplicationContext
的主要功能是註冊、管理 bean 的定義與別名
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {}
BeanDefinitionRegistry
這個介面主要定義了註冊 bean 的定義及別名
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
如何實現這些介面的
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
上
4. GenericWebApplicationContext
GenericWebApplicationContext
的主要功能是新增了設定 bean 配置檔案來源,允許通過配置的方式例項化上下文環境
public class GenericWebApplicationContext extends GenericApplicationContext
implements ConfigurableWebApplicationContext, ThemeSource {}
ConfigurableWebApplicationContext
public interface ConfigurableWebApplicationContext {
// 設定bean配置檔案來源
void setConfigLocation(String configLocation);
// 設定多個bean配置檔案來源
void setConfigLocations(String... configLocations);
// 獲取bean配置檔案來源
String[] getConfigLocations();
}
ConfigurableWebApplicationContext
擴充套件了WebApplicationContext
,定義了允許通過配置的方式例項化上下文環境
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
來構建內嵌的上下文;一般很少用到
5. StaticWebApplicationContext
因為 StaticApplicationContext
實現功能比較少,放在這裡一起解析
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
實現功能也比較少
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
的核心方法,也就不能通過檔案載入配置,
該類設計的目的主要用於測試,不用於產品環境
6. AbstractRefreshableApplicationContext
AbstractRefreshableApplicationContext
的主要功能是建立 bean 工廠,重新整理上下文資料
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;
}
7. AbstractRefreshableConfigApplicationContext
AbstractRefreshableConfigApplicationContext
的主要功能是可以通過檔案載入配置
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
如何是解析路徑的,後面再解析
8. XmlWebApplicationContext
因為 AbstractRefreshableWebApplicationContext
實現功能比較少,放在這裡一起解析
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
的主要功能是定義了預設的配置檔案,建立一個 bean 定義的 xml 解析器,並註冊 bean 定義
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 個問題:
- 定義了預設的配置檔案,有 servlet-name(如
testapp
),用字首字尾包裹為/WEB-INF/testapp-servlet.xml
,如果沒有 servlet-name,則為/WEB-INF/applicationContext.xml
- 建立一個 bean 定義的 xml 解析器,並通過配置檔案載入 bean 定義
SpringMVC 框架的預設載入機制便是使用XmlWebApplicationContext
作為上下文環境,從 xml
檔案載入配置與 bean 定義
至於XmlBeanDefinitionReader
如何是解析 bean 定義的,後面再解析
9. AnnotationConfigWebApplicationContext
AnnotationConfigWebApplicationContext
的主要功能是可以通過註解載入配置和 bean 定義
public class AnnotationConfigWebApplicationContext extends AbstractRefreshableWebApplicationContext
implements AnnotationConfigRegistry {}
public interface AnnotationConfigRegistry {
// 根據類名註冊元件
void register(Class<?>... componentClasses);
// 根據包名掃描元件
void scan(String... basePackages);
}
這兩個方法正好是通過註解如 @Configuration, @bean, @Component, @Controller, @Service
等註冊 bean 的底層機制
來看看 AnnotationConfigWebApplicationContext
是如何實現的
public class AnnotationConfigWebApplicationContext extends AbstractRefreshableWebApplicationContext
implements AnnotationConfigRegistry {
// 元件類集合
private final Set<Class<?>> componentClasses = new LinkedHashSet<>();
// 掃描包名集合
private final Set<String> 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
完成,這兩個類後面再解析
10. GroovyWebApplicationContext
GroovyWebApplicationContext
的執行機制與 XmlWebApplicationContext
差不多,從 groovy
檔案載入配置與 bean 定義
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};
}
}
}
11. 綜述
WebApplicationContext
定義了 Web 應用初始化的基本流程,主要有 5 個實現類,常用的是:基於 Xml 載入的XmlWebApplicationContext
與基於註解載入的AnnotationConfigWebApplicationContext
- GenericWebApplicationContext、StaticWebApplicationContext
:這兩者都只是WebApplicationContext
的基礎實現,都不能通過配置檔案和註解載入應用配置與 bean,一般用於擴充套件實現(如 SpringBoot),很少直接使用 - XmlWebApplicationContext
:基於 XML 載入應用配置與 bean 的上下文環境,是 SpringMVC 的預設 Context - AnnotationConfigWebApplicationContext
:基於註解如@Configuration, @bean
等載入應用配置與 bean 的上下文環境 - GroovyWebApplicationContext
:與XmlWebApplicationContext
的實現差不多,但可以用 Groovy 代替 xml 做配置檔案,但目前 Groovy 遠不及 Xml 普及,用的仍然不多
12. 未完
這一節仍然有一些點留待下次解析:
ConfigurableListableBeanFactory
如何載入、例項化 beanResourceEditorRegistrar
如何註冊屬性編輯器、屬性編輯器如何解析為物件PathMatchingResourcePatternResolver
如何解析、載入 locationPattern 指定的資源PropertySourcesPropertyResolver
如何是解析路徑的XmlBeanDefinitionReader
如何是解析 bean 定義的AnnotatedBeanDefinitionReader
是如何註冊 bean 的ClassPathBeanDefinitionScanner
是如何掃描包的
後續
更多部落格,檢視 https://github.com/senntyou/blogs
版權宣告:自由轉載-非商用-非衍生-保持署名(創意共享 3.0 許可證)