关于后端:Spring-源码解析二上下文组件WebApplicationContext

8次阅读

共计 25465 个字符,预计需要花费 64 分钟才能阅读完成。

Spring 源码解析二:上下文组件(WebApplicationContext)

上一篇解析了 DispatcherServletContextLoaderListener 这两个类,解析了利用初始化与申请解决的流程,但还有一些组件须要解析:

  • ConfigurableWebApplicationContext.refresh 刷新上下文
  • ApplicationContext.getBean 从上下文中获取 bean
  • DispatcherServlet.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 个问题:

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

public class AnnotationConfigWebApplicationContext extends AbstractRefreshableWebApplicationContext
        implements AnnotationConfigRegistry {}

先来看看 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如何加载、实例化 bean
  • ResourceEditorRegistrar如何注册属性编辑器、属性编辑器如何解析为对象
  • PathMatchingResourcePatternResolver如何解析、加载 locationPattern 指定的资源
  • PropertySourcesPropertyResolver如何是解析门路的
  • XmlBeanDefinitionReader如何是解析 bean 定义的
  • AnnotatedBeanDefinitionReader是如何注册 bean 的
  • ClassPathBeanDefinitionScanner是如何扫描包的

后续

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

作者:深予之 (@senntyou)

版权申明:自在转载 - 非商用 - 非衍生 - 放弃署名(创意共享 3.0 许可证)

正文完
 0