3.Spring5底层原理之ApplicationContext实现.md

spring / 2022-04-15

这一章我们来看看创建ApplicationContext的几种方法。

首先我们创建一个类进行测试,内容如下:

public class A02Application {
    public static void main(String[] args) {
        testClassPathXmlApplicationContext();
    }

    // 较为经典的容器,基于classpath下xml格式的配置文件来创建
    private static void testClassPathXmlApplicationContext(){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("b01.xml");
        for (String beanDefinitionName : context.getBeanDefinitionNames()) {
            System.out.println(beanDefinitionName);
        }

        System.out.println(context.getBean(Bean2.class).getBean1());
    }

    static class Bean1{
		
    }

    static class Bean2{
        private Bean1 bean1;

        public void setBean1(Bean1 bean1){
            this.bean1 = bean1;
        }

        public Bean1 getBean1(){
            return bean1;
        }
    }
}

A02Application类中,有Bean1,Bean2两个类,Bean2依赖Bean1。

基于classpath下xml格式的配置文件来创建

testClassPathXmlApplicationContext()方法测试利用xml进行创建ApplicationContext,这是一种很经典的用法,但是现在基本上很少用了,我们在resources下创建Spring的配置文件b01.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="com.zhaojun.springsource.a02.A02Application$Bean1" id="bean1"/>

    <bean class="com.zhaojun.springsource.a02.A02Application$Bean2" id="bean2">
        <property name="bean1" ref="bean1"/>
    </bean>
</beans>

相信以前用xml配置spring的同学应该明白上面的配置是什么意思。就是定义了两个bean,分别是bean1和bean2,其中bean2依赖bean1。

我们运行main方法,调用testClassPathXmlApplicationContext,输出结果如下

10:37:51.207 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@2a70a3d8
10:37:51.485 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 2 bean definitions from class path resource [b01.xml]
10:37:51.516 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean1'
10:37:51.528 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean2'
bean1
bean2
com.zhaojun.springsource.a02.A02Application$Bean1@2af004b

从日志中可以看到,ClassPathXmlApplicationContext从b01.xml中加载了两个bean定义,然后创建了两个Bean

基于磁盘路径下xml格式的配置文件来创建

不仅可以基于classpath下xml来创建,不在classpath下么,可以用FileSystemXmlApplicationContext来创建

// 基于磁盘路径下xml格式的配置文件来创建
private static void testFileSystemXmlApplicationContext(){
    FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("src/main/resources/b01.xml");

    for (String beanDefinitionName : context.getBeanDefinitionNames()) {
        System.out.println(beanDefinitionName);
    }

    System.out.println(context.getBean(Bean2.class).getBean1());
}

和classpath差不多,只不过就是换了加载路径,这里就不过多赘述了。

基于java配置类来创建

首先我们在A02Application中创建一个配置类

@Configuration
static class Config{

    @Bean
    public Bean1 bean1(){
        return new Bean1();
    }

    @Bean
    public Bean2 bean2(Bean1 bean1){
        Bean2 bean2 = new Bean2();
        bean2.setBean1(bean1);
        return bean2;
    }

}

然后创建AnnotationConfigApplicationContext

// 较为经典的容器,基于java配置类来创建
private static void testAnnotationConfigApplicationContext(){
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);

    for (String beanDefinitionName : context.getBeanDefinitionNames()) {
        System.out.println(beanDefinitionName);
    }

    System.out.println(context.getBean(Bean2.class).getBean1());
}

输出日志

11:28:42.872 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@543c6f6d
11:28:42.890 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
11:28:42.992 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
11:28:42.993 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
11:28:42.995 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
11:28:42.996 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
11:28:43.003 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'a02Application.Config'
11:28:43.008 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean1'
11:28:43.019 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean2'
11:28:43.021 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Autowiring by type from bean name 'bean2' via factory method to bean named 'bean1'
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
a02Application.Config
bean1
bean2
com.zhaojun.springsource.a02.A02Application$Bean1@431cd9b2

Process finished with exit code 0

可以看到,利用AnnotationConfigApplicationContext会帮我们创建后处理器,在上一章我们讲过,后处理器能帮我们处理注解来创建bean。

基于java配置类来创建,用于web环境

// 较为经典的容器,基于java配置类来创建,用于web环境
private static void testAnnotationConfigServletWebServer() {
    AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);

}

我们还需要一个WebConfig

@Configuration
static class WebConfig {
    @Bean
    public ServletWebServerFactory servletWebServerFactory() {
        return new TomcatServletWebServerFactory();
    }

    @Bean
    public DispatcherServlet dispatcherServlet() {
        return new DispatcherServlet();
    }

    @Bean
    public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(DispatcherServlet dispatcherServlet) {
        return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
    }

    @Bean("/hello")
    public Controller controller1(){
        return new Controller() {
            @Override
            public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
                response.getWriter().println("hello");
                return null;
            }
        };
    }
}

首先需要一个web容器,我们创建内嵌的tomcat容器,然后我们知道springmvc的入口是DispatcherServlet,然后我们把DispatcherServlet注册到web容器上,“/”表示对所有请求进行处理。

最后,我们创建一个Controller,用来实验我们是否创建成功。运行main方法。查看日志

12:54:09.033 [main] DEBUG org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext - Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@3578436e
12:54:09.090 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
12:54:09.195 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
12:54:09.197 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
12:54:09.198 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
12:54:09.199 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
12:54:09.205 [main] DEBUG org.springframework.ui.context.support.UiApplicationContextUtils - Unable to locate ThemeSource with name 'themeSource': using default [org.springframework.ui.context.support.ResourceBundleThemeSource@4b520ea8]
12:54:09.206 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'servletWebServerFactory'
12:54:09.207 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'a02Application.WebConfig'
12:54:10.164 [main] DEBUG org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory - Code archive: D:\repository\org\springframework\boot\spring-boot\2.6.4\spring-boot-2.6.4.jar
12:54:10.164 [main] DEBUG org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory - Code archive: D:\repository\org\springframework\boot\spring-boot\2.6.4\spring-boot-2.6.4.jar
12:54:10.164 [main] DEBUG org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory - None of the document roots [src/main/webapp, public, static] point to a directory and will be ignored.
12:54:10.216 [main] INFO org.springframework.boot.web.embedded.tomcat.TomcatWebServer - Tomcat initialized with port(s): 8080 (http)
4月 15, 2022 12:54:10 下午 org.apache.coyote.AbstractProtocol init
信息: Initializing ProtocolHandler ["http-nio-8080"]
4月 15, 2022 12:54:10 下午 org.apache.catalina.core.StandardService startInternal
信息: Starting service [Tomcat]
4月 15, 2022 12:54:10 下午 org.apache.catalina.core.StandardEngine startInternal
信息: Starting Servlet engine: [Apache Tomcat/9.0.58]
4月 15, 2022 12:54:10 下午 org.apache.catalina.core.ApplicationContext log
信息: Initializing Spring embedded WebApplicationContext
12:54:10.415 [main] DEBUG org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext - Published root WebApplicationContext as ServletContext attribute with name [org.springframework.web.context.WebApplicationContext.ROOT]
12:54:10.415 [main] INFO org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext - Root WebApplicationContext: initialization completed in 1383 ms
12:54:10.418 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'dispatcherServletRegistrationBean'
12:54:10.421 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'dispatcherServlet'
12:54:10.446 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Autowiring by type from bean name 'dispatcherServletRegistrationBean' via factory method to bean named 'dispatcherServlet'
12:54:10.458 [main] DEBUG org.springframework.boot.web.servlet.ServletContextInitializerBeans - Mapping filters: 
12:54:10.458 [main] DEBUG org.springframework.boot.web.servlet.ServletContextInitializerBeans - Mapping servlets: dispatcherServlet urls=[/]
12:54:10.475 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean '/hello'
12:54:10.491 [main] DEBUG org.springframework.context.support.DefaultLifecycleProcessor - Starting beans in phase 2147483646
4月 15, 2022 12:54:10 下午 org.apache.coyote.AbstractProtocol start
信息: Starting ProtocolHandler ["http-nio-8080"]
12:54:10.513 [main] INFO org.springframework.boot.web.embedded.tomcat.TomcatWebServer - Tomcat started on port(s): 8080 (http) with context path ''
12:54:10.514 [main] DEBUG org.springframework.context.support.DefaultLifecycleProcessor - Successfully started bean 'webServerStartStop'
12:54:10.515 [main] DEBUG org.springframework.context.support.DefaultLifecycleProcessor - Starting beans in phase 2147483647
12:54:10.515 [main] DEBUG org.springframework.context.support.DefaultLifecycleProcessor - Successfully started bean 'webServerGracefulShutdown'

通过日志我们可以发现,在8080端口,启动了一个web服务,我们访问http://localhost:8080/hello。效果如下图:

image-20220415125534565

总结

本章我们了解了ApplicationContext常见的四种实现。