1

Spring source code analysis 2: Context component (WebApplicationContext)

The previous article analyzed the DispatcherServlet and ContextLoaderListener , and analyzed the application initialization and request processing flow, but there are still some components that need to be resolved:

  • ConfigurableWebApplicationContext.refresh refresh context
  • ApplicationContext.getBean Get the bean from the context
  • The policy processing defined in the DispatcherServlet.properties
  • The policy processing defined in the ContextLoader.properties
  • View.render view rendering

In this chapter, take a look at the policy processing defined in the ContextLoader.properties

Only one strategy is defined in the ContextLoader.properties

org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

By default, XmlWebApplicationContext (based on XML loading) is used as the application context

spring-web internally defines 5 application context classes:

  • GenericWebApplicationContext
    : WebApplicationContext , but the application configuration and beans cannot be loaded through configuration files and annotations. It is generally used to extend the implementation (such as SpringBoot) and is rarely used directly
  • StaticWebApplicationContext
    : It is also WebApplicationContext , but does not support i18n. It is mainly used for testing and does not require a product environment
  • XmlWebApplicationContext
    WebApplicationContext implementation of loading application configuration and bean based on XML, which is the default Context of SpringMVC
  • AnnotationConfigWebApplicationContext
    : Based on annotations such as @Configuration, @bean load application configuration and bean WebApplicationContext implementation
  • GroovyWebApplicationContext
    : It is XmlWebApplicationContext the implementation of 06177d1b2d8d4c, but Groovy can be used instead of xml as the configuration file, which is not used much at present

Let's first take a look at the inheritance relationship of these 5 application context classes

- 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

We can find that each class inherits AbstractApplicationContext , and XmlWebApplicationContext , AnnotationConfigWebApplicationContext ,
GroovyWebApplicationContext all inherit AbstractRefreshableWebApplicationContext

1. DefaultResourceLoader

DefaultResourceLoader
The main function is to implement resource loading

public class DefaultResourceLoader implements ResourceLoader {}

First look at the interface ResourceLoader

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

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

DefaultResourceLoader
Is the default implementation 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
The main function of is to obtain bean instances by name, type or annotation, obtain contextual environment objects and resources, and refresh contextual data

public abstract class AbstractApplicationContext extends DefaultResourceLoader
        implements ConfigurableApplicationContext {}

Interface ConfigurableApplicationContext
And its inherited interfaces mainly define the following methods

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
Get the environment

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

Because different Context register beans in getBeanFactory left to subclasses to implement

2.3. AbstractApplicationContext.getBeansOfType

AbstractApplicationContext.getBeansOfType
Get bean by type or annotation

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
Refresh context data

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

        // ... 代码省略
    }
}
  • Because different Context register beans in refreshBeanFactory, postProcessBeanFactory left to subclasses to implement
  • ConfigurableListableBeanFactory how to load and instantiate the bean, and then analyze it later

2.5. AbstractApplicationContext.prepareBeanFactory

AbstractApplicationContext.prepareBeanFactory
Prepare the bean factory

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 How to register the property editor, how to parse the property editor as an object, and then resolve it later

2.6. AbstractApplicationContext.getResources

AbstractApplicationContext.getResources
Get multiple resources based on locationPattern, such as wildcard *

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 How to parse and load the resource specified by locationPattern, and then parse it later

2.7. Summary

In general, the AbstractApplicationContext class completes most of the functions of the context environment, including environment loading, bean loading and pre- and post-processing, event dispatch, completion of some initialization work, etc.
However, several interfaces are extended to implement subclasses, such as how to load, register, and instantiate beans, etc.

3. GenericApplicationContext

GenericApplicationContext
The main function is to register and manage bean definitions and aliases

public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {}

BeanDefinitionRegistry
This interface mainly defines the definition and alias of the registered 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);
}

Take a look at how GenericApplicationContext implements these interfaces

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

Finally settled on beanFactory

4. GenericWebApplicationContext

GenericWebApplicationContext
The main function is to add the source of the configuration bean configuration file, allowing the context environment to be instantiated by configuration

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 extends WebApplicationContext and defines the context environment allowed to be instantiated by configuration

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 does not implement ConfigurableWebApplicationContext , so the configuration cannot be loaded through files.
The purpose of this type of design is not web.xml , but to install programmatically. For example, use WebApplicationInitializers to build an embedded context; generally it is rarely used

5. StaticWebApplicationContext

Because StaticApplicationContext
There are few implementation functions, so put it here and analyze it together

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
Few implementation functions

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 also does not implement ConfigurableWebApplicationContext , so the configuration cannot be loaded through files.
The purpose of this type of design is mainly for testing, not for production environments

6. AbstractRefreshableApplicationContext

AbstractRefreshableApplicationContext
The main function is to create a bean factory, refresh the context data

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
The main function is to load the configuration through the file

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 implements the ConfigurableWebApplicationContext , that is, you can load the configuration file
  • PropertySourcesPropertyResolver resolve the path, and then resolve it later

8. XmlWebApplicationContext

Because AbstractRefreshableWebApplicationContext
There are few implementation functions, so put it here and analyze it together

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
The main function is to define the default configuration file, create a bean definition xml parser, and register the bean definition

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 mainly solves 2 problems:

  1. The default configuration file is defined, with a servlet-name (such as testapp ), which is wrapped with a prefix and suffix as /WEB-INF/testapp-servlet.xml , if there is no servlet-name, it is /WEB-INF/applicationContext.xml
  2. Create a bean definition xml parser, and load the bean definition through the configuration file

The default loading mechanism of the SpringMVC framework is to use XmlWebApplicationContext as the context environment to load configuration and bean definitions xml

As for XmlBeanDefinitionReader parses the bean definition, we will parse it later

9. AnnotationConfigWebApplicationContext

AnnotationConfigWebApplicationContext
The main function is to load configuration and bean definitions through annotations

public class AnnotationConfigWebApplicationContext extends AbstractRefreshableWebApplicationContext
        implements AnnotationConfigRegistry {}

First take a look at AnnotationConfigRegistry

public interface AnnotationConfigRegistry {
    // 根据类名注册组件
    void register(Class<?>... componentClasses);

    // 根据包名扫描组件
    void scan(String... basePackages);
}

These two methods happen to be the underlying mechanism of registering beans @Configuration, @bean, @Component, @Controller, @Service

AnnotationConfigWebApplicationContext 's see how 06177d1b2da197 is implemented

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

In fact, the registered bean is AnnotatedBeanDefinitionReader , and the scanning package is ClassPathBeanDefinitionScanner . These two classes will be resolved later

10. GroovyWebApplicationContext

GroovyWebApplicationContext
The operating mechanism is similar to XmlWebApplicationContext , loading configuration and bean definition groovy

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. Summary

WebApplicationContext
Defines the basic process of Web application initialization, there are mainly 5 implementation classes, the commonly used ones are: XmlWebApplicationContext
AnnotationConfigWebApplicationContext based on annotation loading

  • GenericWebApplicationContextStaticWebApplicationContext
    : Both of these are just WebApplicationContext , neither can load application configuration and beans through configuration files and annotations. They are generally used to extend implementations (such as SpringBoot) and are rarely used directly.
  • XmlWebApplicationContext
    : Load application configuration and bean context based on XML, which is the default Context of SpringMVC
  • AnnotationConfigWebApplicationContext
    : Load application configuration and bean context based on annotations such as @Configuration, @bean
  • GroovyWebApplicationContext
    : It is XmlWebApplicationContext the implementation of 06177d1b2da655, but Groovy can be used instead of xml as the configuration file, but currently Groovy is far less popular than Xml, and it is still not used much

12. Unfinished

There are still some points in this section for the next analysis:

  • ConfigurableListableBeanFactory How to load and instantiate beans
  • ResourceEditorRegistrar How to register the attribute editor, how to resolve the attribute editor into an object
  • PathMatchingResourcePatternResolver How to parse and load the resource specified by locationPattern
  • PropertySourcesPropertyResolver resolve the path
  • XmlBeanDefinitionReader how to parse bean definition
  • AnnotatedBeanDefinitionReader how to register bean
  • ClassPathBeanDefinitionScanner scans the package

Follow-up

For more blogs, check out https://github.com/senntyou/blogs

Author: Shen Yuzhi (@senntyou)

Copyright statement: Freely reprinted-non-commercial-non-derivative-keep the signature ( Creative Commons 3.0 License )


深雨
12.7k 声望6.5k 粉丝

达则兼济天下,穷则独善其身。