Spring源码(五):深入了解ApplicationContext

40 阅读4分钟

前面都是基于Spring的bean模块的BeanFactory接口及其默认实现类DefaultListableBeanFactory为例进行分析的,这次我们看下context模块的另一个重要的接口ApplicationContext。

一、类结构

ApplicationContext和BeanFactory两者都是用于加载bean的,但是相比之下,ApplicationContext提供了更多的扩展功能,简单一点说:ApplicationContext包含BeanFactory的所有功能。通常建议比BeanFactory优先使用。

我们先从类结构出发,如图

ApplicationContext类图

代码如下

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
        MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
​
    // 唯一标识id
    String getId();
​
    String getApplicationName();
​
​
    String getDisplayName();
​
    long getStartupDate();
​
    // ApplicationContext天生具有继承关系
    ApplicationContext getParent();
​
    // 天生兼容BeanFactory,且是可以支持注入的BeanFactory
    AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
}

下面对其父级进行说明。

二、父接口

1、MessageSource

实现信息资源国际化的接口。

// 天然支持多语言、国际化
public interface MessageSource {
​
    String getMessage(String code, Object[] args, String defaultMessage, Locale locale);
​
    String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException;
​
    String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;
​
}

2、ApplicationEventPublisher

事件发布的接口。

// 支持事件发布,可以在spring启动中,触发响应的事件监听
public interface ApplicationEventPublisher {
​
    void publishEvent(ApplicationEvent event);
​
}

3、ResourcePatternResolver

资源模式解析器的接口,继承自ResourceLoader接口。

// 加载类及类资源文件接口
public interface ResourcePatternResolver extends ResourceLoader {
​
    String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
​
    Resource[] getResources(String locationPattern) throws IOException;
​
}
​
public interface ResourceLoader {
​
    /** Pseudo URL prefix for loading from the class path: "classpath:" */
    String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
​
​
    Resource getResource(String location);
​
    ClassLoader getClassLoader();
​
}

4、EnvironmentCapable

Spring环境获取的接口。

// 支持spring环境变量、配置获取
public interface EnvironmentCapable {
​
    /**
     * Return the {@link Environment} associated with this component.
     */
    Environment getEnvironment();
​
}

5、继承自BeanFactory的接口

还有两个接口分别是

  • ListableBeanFactory:可列举的bean工厂接口,定义了大量获取bean的方法。
  • HierarchicalBeanFactory:分层的bean工厂接口。

三、子接口

ApplicationContext只有三个子接口,而两个子接口下面有众多的子孙接口及实现类(Ctrl + h 可以查看类自上而下继承结构)。

1、ConfigurableApplicationContext

可配置的ApplicationContext接口。

// 主要是服务于ApplicationContext的全生命周期,加载配置、环境变量、应用BeanFactoryPostProcessor
// 发布spring事件、刷新ApplicationContext、销毁ApplicationContext
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {
​
    String CONFIG_LOCATION_DELIMITERS = ",; \t\n";
​
    String CONVERSION_SERVICE_BEAN_NAME = "conversionService";
​
    String LOAD_TIME_WEAVER_BEAN_NAME = "loadTimeWeaver";
​
    String ENVIRONMENT_BEAN_NAME = "environment";
​
    String SYSTEM_PROPERTIES_BEAN_NAME = "systemProperties";
​
    String SYSTEM_ENVIRONMENT_BEAN_NAME = "systemEnvironment";
​
    void setId(String id);
​
    void setParent(ApplicationContext parent);
​
    ConfigurableEnvironment getEnvironment();
​
    void setEnvironment(ConfigurableEnvironment environment);
​
    void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor);
​
    void addApplicationListener(ApplicationListener<?> listener);
​
    void refresh() throws BeansException, IllegalStateException;
​
    void registerShutdownHook();
​
    void close();
​
    boolean isActive();
​
    ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
}

2、WebApplicationContext

适用于Web环境的ApplicationContext接口,常见于比较原始Spring MVC场景。

// 与web环境相结合,适配web协议 javax.servlet.ServletContext
public interface WebApplicationContext extends ApplicationContext {
​
    String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
​
    // 各种web作用域
    String SCOPE_REQUEST = "request";
​
    String SCOPE_SESSION = "session";
​
    String SCOPE_GLOBAL_SESSION = "globalSession";
​
    String SCOPE_APPLICATION = "application";
​
    String SERVLET_CONTEXT_BEAN_NAME = "servletContext";
​
    String CONTEXT_PARAMETERS_BEAN_NAME = "contextParameters";
​
    String CONTEXT_ATTRIBUTES_BEAN_NAME = "contextAttributes";
​
    // 想起servlet了吗?
    // 想起xml了吗?
    ServletContext getServletContext();
}

3、WebServerApplicationContext

WebServerApplicationContext是Spring Boot专用ApplicationContext。

// 支持内嵌tomcat
// 这是一条广告,欢迎关注公众号:好看的HK
public interface WebServerApplicationContext extends ApplicationContext {
​
    WebServer getWebServer();
​
    // 支持多服务多端口部署
    String getServerNamespace();
}
​
// 服务器接口
public interface WebServer {
​
    void start() throws WebServerException;
​
    void stop() throws WebServerException;
​
    int getPort();
}

4、ApplicationContextAware

@Component
public class Test implements ApplicationContextAware {
​
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        // 真正干活的是 AnnotationConfigServletWebServerApplicationContext
        System.out.println(applicationContext.getClass());
        // true
        System.out.println(applicationContext instanceof WebServerApplicationContext);
        // true
        System.out.println(applicationContext instanceof WebApplicationContext);
    }
}

四、重要实现类

ApplicationContext接口功能远比BeanFactory接口强大,因此如果我们想要一个精简版的Spring环境(比如只想要IoC、不想要AOP或Web环境等特殊情况下),可以使用这些开箱即用的ApplicationContext。

先回忆一下远古的xml配置方式(这个配置文件后面还会出现)。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
    <aop:aspectj-autoproxy/>
    <bean id="testBean" class="xiaokui1.TestBean"/>
    <bean class="xiaokui1.AspectJTest"/>
</beans>

啊,这该死的回忆又涌上了心头!

1、ClassPathXmlApplicationContext

public static void main(String[] args) {
    // 使用相对类路径 ClassPathXmlApplicationContext
    // 固定文件路径的话,可以用 FileSystemXmlApplicationContext
    // 区别在 Resource 接口实现的不同
    ClassPathXmlApplicationContext application = new ClassPathXmlApplicationContext("xiaokui1/xiaokui.xml");
    TestBean testBean = (TestBean)application.getBean("testBean");
    testBean.test();
}

2、AnnotationConfigApplicationContext

@SpringBootApplication
public class TestApp {
​
    public static void main(String[] args) {
        // web环境可以考虑用AnnotationConfigWebApplicationContext,但单独使用会有环境配置问题,不能建议直接使用
        // AnnotationConfigApplicationContext相对来说轻量级一点
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        // 扫描特定包下面类
        context.scan("site.xiaokui.app");
        // 刷新,触发bean的加载
        context.refresh();
        Object bean1 = context.getBean(TestController.class);
        // 正常输出 class site.xiaokui.app.controller.TestController
        System.out.println(bean1.getClass());
        Object bean2 = context.getBean(InitPrometheusData.class);
        // 正常输出 class site.xiaokui.app.task.InitPrometheusData$$EnhancerBySpringCGLIB$$ae07d98f
        System.out.println(bean2.getClass());
​
//        SpringApplication.run(TestApp.class, args);
//        log.info("========== web服务已成功启动!!! ==========");
    }
}