Skip to content

Spring 源码解析二:上下文组件(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 :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 的主要功能是实现资源加载

java
public class DefaultResourceLoader implements ResourceLoader {}

先来看看接口ResourceLoader

java
public interface ResourceLoader {
    // 根据一个字符位置信息获取资源
    Resource getResource(String location);

    // 获取资源加载器
    ClassLoader getClassLoader();
}

DefaultResourceLoader 是 ResourceLoader 的默认实现

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);
            }
        }
    }
}

2. AbstractApplicationContext ​

AbstractApplicationContext 的主要功能是通过名字、类型或注解获取 bean 实例,获取上下文的环境对象与资源、刷新上下文数据

java
public abstract class AbstractApplicationContext extends DefaultResourceLoader
        implements ConfigurableApplicationContext {}

接口 ConfigurableApplicationContext 及其继承的接口主要定义以下的方法

java
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 获取环境

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();
    }
}

2.2. AbstractApplicationContext.getBean ​

AbstractApplicationContext.getBean 获取 bean

java
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

java
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 刷新上下文数据

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,后面再解析

2.5. AbstractApplicationContext.prepareBeanFactory ​

AbstractApplicationContext.prepareBeanFactory 预备 bean 工厂

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()));

        // 添加一个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 获取多个资源,如通配符*

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 指定的资源,后面再解析

2.7. 综述 ​

总的来说,AbstractApplicationContext 类完成上下文环境的大部分功能,包括环境加载、bean 的加载与前置后置处理、事件派发、完成一些初始化工作等, 但扩展了几个接口给子类实现,如如何加载、注册、实例化 bean 等

3. GenericApplicationContext ​

GenericApplicationContext 的主要功能是注册、管理 bean 的定义与别名

java
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {}

BeanDefinitionRegistry 这个接口主要定义了注册 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 上

4. GenericWebApplicationContext ​

GenericWebApplicationContext 的主要功能是添加了设置 bean 配置文件来源,允许通过配置的方式实例化上下文环境

java
public class GenericWebApplicationContext extends GenericApplicationContext
        implements ConfigurableWebApplicationContext, ThemeSource {}

ConfigurableWebApplicationContext

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来构建内嵌的上下文;一般很少用到

5. StaticWebApplicationContext ​

因为 StaticApplicationContext 实现功能比较少,放在这里一起解析

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 实现功能也比较少

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 的核心方法,也就不能通过文件加载配置, 该类设计的目的主要用于测试,不用于产品环境

6. AbstractRefreshableApplicationContext ​

AbstractRefreshableApplicationContext 的主要功能是创建 bean 工厂,刷新上下文数据

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;
}

7. AbstractRefreshableConfigApplicationContext ​

AbstractRefreshableConfigApplicationContext 的主要功能是可以通过文件加载配置

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如何是解析路径的,后面再解析

8. XmlWebApplicationContext ​

因为 AbstractRefreshableWebApplicationContext 实现功能比较少,放在这里一起解析

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 的主要功能是定义了默认的配置文件,创建一个 bean 定义的 xml 解析器,并注册 bean 定义

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
  2. 创建一个 bean 定义的 xml 解析器,并通过配置文件载入 bean 定义

SpringMVC 框架的默认加载机制便是使用XmlWebApplicationContext作为上下文环境,从 xml 文件加载配置与 bean 定义

至于XmlBeanDefinitionReader如何是解析 bean 定义的,后面再解析

9. AnnotationConfigWebApplicationContext ​

AnnotationConfigWebApplicationContext 的主要功能是可以通过注解载入配置和 bean 定义

java
public class AnnotationConfigWebApplicationContext extends AbstractRefreshableWebApplicationContext
        implements AnnotationConfigRegistry {}

先来看看 AnnotationConfigRegistry

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<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 定义

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};
        }
    }
}

11. 综述 ​

WebApplicationContext 定义了 Web 应用初始化的基本流程,主要有 5 个实现类,常用的是:基于 Xml 加载的XmlWebApplicationContext 与基于注解加载的AnnotationConfigWebApplicationContext

12. 未完 ​

这一节仍然有一些点留待下次解析:

  • ConfigurableListableBeanFactory如何加载、实例化 bean
  • ResourceEditorRegistrar如何注册属性编辑器、属性编辑器如何解析为对象
  • PathMatchingResourcePatternResolver如何解析、加载 locationPattern 指定的资源
  • PropertySourcesPropertyResolver如何是解析路径的
  • XmlBeanDefinitionReader如何是解析 bean 定义的
  • AnnotatedBeanDefinitionReader是如何注册 bean 的
  • ClassPathBeanDefinitionScanner是如何扫描包的

后续 ​

更多博客,查看 https://github.com/deepraining/blogs

作者:深雨 (@deepraining)

版权声明:自由转载-非商用-非衍生-保持署名(创意共享 3.0 许可证)