Spring Boot(6) 原理和启动流程四、ApplicationContextInitializer、ApplicationContextAware、ApplicationContext、App

2022-04-14 17:05:15 浏览数 (1)

SpringBoot是一个快速开发框架,目的是解放java程序猿的生产力,提高开发效率。主要特点: 1、整合依赖:通过Maven,快速的将一些常用的第三方依赖整合。 2、简化配置:简化XML配置,全部采用注解形式。 3、集成web容器:内置Http服务器(Jetty和Tomcat),最终以java应用程序进行执行。 简化Spring应用的创建、运行、调试、部署的工作,使用它可以做到专注于Spring应用的开发,而无需过多关注XML的配置。

网上一大推关于Spring Boot启动相关的文章,我这里只是之前学习笔记的总结。

一、核心原理

1. 整合SpingMVC框架和Web容器

SpringBoot核心通过Maven继承依赖关系快速整合第三方框架

代码语言:javascript复制
<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>2.0.0.RELEASE</version>	
</parent>
<dependencies>
	<!-- SpringBoot 整合SpringMVC -->
	<!-- 我们依赖spring-boot-starter-web能够帮我整合Spring环境 原理通过Maven子父工程 -->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
</dependencies>

spring boot 通过引用spring-boot-starter-web依赖,整合SpingMVC等相关框架。我们看spring-boot-starter-web-1.5.2版本(目前Spring Boot版本是 2.1.xx-SNAPSHOT),整合的主要依赖: spring-boot-starter :这是Spring Boot的核心启动器,包含了自动配置、日志和YAML); spring-boot-starter-tomcat: Tomcat容器 spring-web : 全栈式Web开发 spring-webmvc:SpingMVC框架

2. 注解@SpringBootApplication启动

基于SpringMVC无配置文件(纯Java)完全注解化实现SpringBoot框架,Main函数启动。

通过SpringApplication类用于引导和启动一个Spring应用程序(即SpringBoot开发的应用)。通常用SpringBoot开发一个应用程序时,在主类的main函数中可以通过如下代码启动一个Spring应用:

代码语言:javascript复制
@SpringBootApplication 
public class Application {
    //方式一
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
    //方式二
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(Application .class);
        app.run(args);
    }
    //方式三
    public static void main(String[] args) {
        new SpringApplicationBuilder()
            .sources(Parent.class)
            .child(Application.class)
            .run(args);
    }
}

springboot中只需要添加@SpringBootApplication注解,通过@SpringBootApplication注解操作整个SpringMVC的初始化过程,并启动应用。

实际上@SpringBootApplication注解只是一个组合注解,包含@Configuration配置类,@ComponentScan包扫描类,@EnableAutoConfiguration。根据需求自动加载相关的bean这三个注解。

方式一静态方法SpringApplication.run启动,但实际还是先需要创建一个 SpringApplication 对象实例,然后调用 SpringApplication实例run方法,其本质上是简化方式二和方式三的启动过程。

方式三是使用SpringApplicationBuilder构造ApplicationContext层次结构,SpringApplicationBuilder可让你链式调用多个方法,还包括 parent和child方法可让你创建一个层次结构。

下面我们结合源码分析初始化启动流程。

启动第一步:构建SpringApplication对象实例

http://www.majunwei.com/view/201708231840127244.html

下面是SpringApplication类静态run方法的源码。可以看到,当我们调用这个静态run方法时,实际上会构造一个SpringApplication实例,然后再调用实例的run方法完成spring应用的启动。

spring boot版本不一样,执行流程差不多一样,只是在细节方面有所不同或者新版新增一些功能。

spring-boot-starter-web-1.5.2版初始化如下:

spring-boot-starter-web-2.1.12版初始化如下,2.x版本主流程没有变化:

在创建SpringApplication对象并初始化spring: (1)设置主类配置 (2)判断应用是不是Web应用,进而根据应用程序的类型创建恰当的ApplicationContext。 (3)初始化指定的ApplicationContextInitializer列表 (4)初始化指定的ApplicationListener列表 (4)判断main class的类名称

我们以新版来说明

1、设置主类配置

SpringApplication的静态方法run(Class<?> primarySource, String... args))的第一个参数primarySource是一个Spring容器配置类(用Java代码对Spring容器进行配置)。第二个参数是命令行参数。

代码语言:javascript复制
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));

SpringApplication能够从各种不同的配置源读取bean的定义。Spring Boot建议采用Java注解配置的方式提供一个全局唯一的配置类。但是,你可以同时使用多种不同的配置源。如果是Java注解的配置方式,会使用AnnotatedBeanDefinitionReader加载配置(通过全类名)。如果是XML的配置方式,则会使用XmlBeanDefinitionReader加载配置(通过XML文件地址)。 如果除了primarySources配置类以外,还需要其它的ApplicationContext配置源,则可以调用SpringApplication#setSources(Set<String> sources)方法进行设置,该方法的参数既可以接受一个配置类的全类名,也可以是一个XML配置文件的地址。

2、判断应用类型

根据 classpath判断是否存在对应的特征类:

  • 如果classpath下存在org.springframework.web.reactive.DispatcherHandler类,则应用类型是REACTIVE
  • 如果classpath下存在org.springframework.web.servlet.DispatcherServlet类,则应用类型是SERVLET
  • 如果上面两个DispatcherServlet类都不存在,则应用类型是NONE
代码语言:javascript复制
this.webApplicationType = WebApplicationType.deduceFromClasspath();
static WebApplicationType deduceFromClasspath() {

        if(ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) &&
                !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) &&
                !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) {
            //reactive类型的web应用
            return REACTIVE;
        } else {
            //
            String[] var0 = SERVLET_INDICATOR_CLASSES;
            int var1 = var0.length;

            for(int var2 = 0; var2 < var1;   var2) {
                String className = var0[var2];
                //非web类应用
                if(!ClassUtils.isPresent(className, (ClassLoader)null)) {
                    return NONE;
                }
            }
            //servlet类型的web应用
            return SERVLET;
        }
    }

3、初始化指定的ApplicationContextInitializer列表

this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));

通过SpringFactoriesLoaderSpringFactoriesLoader读取META-INF/spring.factories文件中的配置

一个工程项目中可以同时有多个META-INF/spring.factories文件(每个jar中都能有一个)。例如在spring-boot-autoconfigure和spring-boot两个jar的META-INF/spring.factories文件中,均有针对ApplicationContextInitializer的配置.spring-boot配置:

我们自己项目也可以设置ApplicationContextInitializer,然后在spring boot启动的时候最开始阶段执行:

4、初始化ApplicationListener监听器列表

代码语言:javascript复制
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));

ApplicationListener和ApplicationContextInitializer初始化过程差不多一样,都是通过SpringFactoriesLoader方式完成。 ApplicationListener是一个接口,里面只有一个onApplicationEvent方法,方法的参数为ApplicationEvent

代码语言:javascript复制
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
   void onApplicationEvent(E event);
}

ApplicationEvent又有一个抽象子类ApplicationContextEvent,ApplicationContextEvent文档的实现类:

ApplicationEvent |-ApplicationContextEvent |-ContextClosedEvent:应用关闭事件 |-ContextRefreshedEvent:应用刷新事件 |-ContextStartedEvent:应用开启事件 |-ContextStoppedEvent:应用停止事件

我们自定义

1、实现ApplicationListener接口:实现接口的onApplicationEvent方法

2、设置监听监听事件:例如ApplicationListener<ContextRefreshedEvent>

3、注册监听:

1)通过@Component、 @Service注解实例化注册监听

代码语言:javascript复制
@Service
public class Log2Initialize implements ApplicationListener<ContextRefreshedEvent> {
    private ApplicationContext applicationContext ;
    /**
     *ApplicationListener 初始化
     *
     * @param event
     * @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent)
     */
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        applicationContext = event.getApplicationContext();
        loadConfig();
    }
}

2)也可以通过SpringApplication类中的addListeners方法将自定义的监听器注册进去

代码语言:javascript复制
public class springBoot2Application {
    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(springBoot2Application.class);
        application.addListeners(new Log2Initialize ());
        application.run(args);

    }
}

5、判断main class的类名称

代码语言:javascript复制
this.mainApplicationClass = this.deduceMainApplicationClass();
主类过程通过遍历异常堆栈找到方法名称是main的类,将其作为主类:
代码语言:javascript复制
private Class<?> deduceMainApplicationClass() {
        try {
            StackTraceElement[] stackTrace = (new RuntimeException()).getStackTrace();
            StackTraceElement[] var2 = stackTrace;
            int var3 = stackTrace.length;

            for(int var4 = 0; var4 < var3;   var4) {
                StackTraceElement stackTraceElement = var2[var4];
                if("main".equals(stackTraceElement.getMethodName())) {
                    return Class.forName(stackTraceElement.getClassName());
                }
            }
        } catch (ClassNotFoundException var6) {
            ;
        }

        return null;
    }

静态方法SpringApplication.run(xx.class)启动都是框架默认的配置,我们可以对SpringApplication进行一些配置,例如关闭Banner,设置一些默认的属性等。下面则是利用 SpringApplicationBuilder 的方式来添加配置:

代码语言:javascript复制
@SpringBootApplication
@ImportResource(locations = {"classpath:applicationContext.xml"})
public class springBoot2Application {
    public static void main(String[] args) throws Exception {
        //SpringApplication.run(springBoot2Application.class, args);
        new SpringApplicationBuilder(springBoot2Application.class)
                // 设置当前应用类型
                .web(WebApplicationType.SERVLET)
                // 设置 banner 横幅打印方式、有关闭、日志、控制台
                .bannerMode(Banner.Mode.OFF)
                // 设置自定义的 banner
                .banner()
                // 追加自定义的 initializer 到集合中
                .initializers()
                // 追加自定义的 listeners 到集合中
                .listeners()
                .run(args);
      }

}

启动第二步:run创建应用


SpringApplication对象构建和初始化完成之后,就开始执行run方法。run()方法启动Spring应用,实质上是为Spring应用创建并初始化Spring上下文。具体代码如下:

代码语言:javascript复制
public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        this.configureHeadlessProperty();
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting();

        Collection exceptionReporters;
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            Banner printedBanner = this.printBanner(environment);
            context = this.createApplicationContext();
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, new Object[]{context});
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            this.refreshContext(context);
            this.afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if(this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }

            listeners.started(context);
            this.callRunners(context, applicationArguments);
        } catch (Throwable var10) {
            this.handleRunFailure(context, var10, exceptionReporters, listeners);
            throw new IllegalStateException(var10);
        }

        try {
            listeners.running(context);
            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var9);
        }
    }

如果要访问SpringApplication.run()的参数,你可以注入org.springframework.boot.ApplicationArguments。ApplicationArguments接口提供对原始String []参数以及解析option和non-option参数的访问:

代码语言:javascript复制
   import org.springframework.boot.*
    import org.springframework.beans.factory.annotation.*
    import org.springframework.stereotype.*
     
    @Component
    public class ArgsBean {
     
        @Autowired
        public ArgsBean(ApplicationArguments args) {
            boolean debug = args.containsOption("debug");
            List<String> files = args.getNonOptionArgs();
            //run with "--debug logfile.txt" debug=true, files=["logfile.txt"]
        }
     
    }

启动流程如下,每个人的理解不一样,所以流程分步骤也不一样:

run方法执开始会先创建一个StopWatch对象,该对象用于统计应用的启动时间。下图中的启动时间日志就是根据StopWatch对象统计的数据打印的。

1. 初始化监听器

会初始化Spring Boot自带的监听器,以及添加到SpringApplication的自定义监听器。

通过SpringFactoriesLoader的机制加载所有的SpringApplicationRunListener。SpringApplicationRunListener的作用就是监听SpringApplication.run方法的各个执行阶段,也可以理解成SpringApplication运行的生命周期。加载完所有的SpringApplicationRunListener后,会将其包装在SpringApplicationRunListeners中,后者就是前者的集合。可以通过调用SpringApplicationRunListeners的API操作所有的SpringApplicationRunListener。 listeners.starting()这句代码通知所有的SpringApplicationRunListener:SpringBoot应用要开始启动。

2、装配参数和环境

初始化main函数参数

3、通知监听器环境准备完成

listeners.environmentPrepared(environment)代码再次通知所有监听器SpringApplicationRunListener:SpringBoot应用使用的Environment准备好。

4、打印banner

打印启动的Banner

启动时打印的banner可以通过在classpath下添加banner.txt来修改,或者通过指定banner.location设置文件。如果这个文件是不寻常编码,你可以设置banner.charset(默认UTF-8)。除了添加文本文件以外,你还可以在classpath下添加banner.gif、banner.jpg或banner.png,或者通过设置banner.image.location属性。图像将被转换成ASCII艺术形式,覆盖所有文本的形式并打印出来。

可以查看spring boot banner的具体配置:

5、创建ApplicationContext

根据初始化时ApplicationType类型,创建对应类型的ApplicationContext。

6、装备Context:

prepareContext方法主要执行:

1、设置Context环境。

2、执行初始化的ApplicationContextInitializer

3、通知监听器执行contextPrepared事件

listeners.contextPrepared(context);即调用SpringApplicationRunListener的contextPrepared方法

4、分别注册springApplicationArguments和springBootBanner这两个bean

5、this.getAllSources()就是获取我们的primarySources和sources

6、this.load(context, sources.toArray(new Object[0]));首先创建BeanDefinitionLoader,设置该loader的sources,annotatedReader,xmlReader,scanner,以及添加scanner的ExcludeFilter(即过滤springboot的启动类),若用户启动的时候设置了beanNameGenerator,resourceLoader,environment的话就替代我们自身设置的属性。同时根据source的类型选择不同的load方法,这边我们是load(class),最终判断是否是component注解,是的话就通过annotatedReader将启动类注册成bean

8.listeners.contextLoaded(context):首先判断ApplicationListener是否属于ApplicationContextAware,如果是的话就将spring容器赋值给该listener,然后将该ApplicationListener赋值给spring容器,然后调用ApplicationListener的onApplicationEvent方法。

7、refreshContext

refreshContext(context):刷新spring容器,装配context beanfactory等非常重要的核心组件。

代码语言:javascript复制
  public void refresh() throws BeansException, IllegalStateException {
        Object var1 = this.startupShutdownMonitor;
        synchronized(this.startupShutdownMonitor) {
            this.prepareRefresh();
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            this.prepareBeanFactory(beanFactory);

            try {
                this.postProcessBeanFactory(beanFactory);
                this.invokeBeanFactoryPostProcessors(beanFactory);
                this.registerBeanPostProcessors(beanFactory);
                this.initMessageSource();
                this.initApplicationEventMulticaster();
                this.onRefresh();
                this.registerListeners();
                this.finishBeanFactoryInitialization(beanFactory);
                this.finishRefresh();
            } catch (BeansException var9) {
                if(this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: "   var9);
                }

                this.destroyBeans();
                this.cancelRefresh(var9);
                throw var9;
            } finally {
                this.resetCommonCaches();
            }

        }
    }

1.this.prepareRefresh();设置些初始的操作比如,开启激活,启动日期,初始化propertySource。

2、this.prepareBeanFactory(beanFactory)

设置beanFactory的classloader,BeanExpressionResolver,PropertyEditorRegistrar,ApplicationContextAwareProcessor和忽略xxxxAware,注册依赖,还有ApplicationListenerDetector ApplicationContextAwareProcessor:只是将applicationContext传递给ApplicationContextAwareProcessor,方便后面的xxxAware调用 忽略xxxxAware:忽略这些Aware接口实现类中与接口set方法中入参类型相同的属性的的自动注入这样就保证了关键的类是由spring容器自己产生的而不是我们注入的,

自动注入不是指的@AutoWire 而是指的是beans的default-autowire="byType" 或在bean的autowire="byType" ,这样spring 回去ioc容器寻找类型相似的类型给其注入,如果实现了spring 的xxaware接口,就不会自动注入记载filterPropertyDescriptorsForDependencyCheck删除与入参类型相同的属性 注册依赖:即指定一些类自动注入的实例是spring指定的实例对象 ApplicationListenerDetector:检测实现了ApplicationListener的实现类,因为有些实现类,无法通过getBeanNamesForType获取到。

3 .postProcessBeanFactory(beanFactory):继续设置ignoreDependencyInterface(ServletContextAware)还有annotatedClasses,basePackages如果存在就设置。

4、invokeBeanFactoryPostProcessors(beanFactory):从beanFactoryPostProcessors获取BeanFactoryPostProcessor,然后先执行BeanDefinitionRegistryPostProcessor类型的postProcessBeanDefinitionRegistry,继续从beanFactory获取BeanDefinitionRegistryPostProcessor类型的bean然后执行postProcessBeanDefinitionRegistry,执行的过程按照PriorityOrdered,Ordered,普通的类型进行执行,然后优先执行registryProcessors的postProcessBeanFactory在执行regularPostProcessors的postProcessBeanFactory,再从BeanFactory获取PriorityOrdered,Ordered,普通的类型三种类型的BeanFactoryPostProcessor,并按照顺序执行。总结:从之前加入的beanFactoryPostProcessor先执行postProcessBeanDefinitionRegistry(假如是BeanDefinitionRegistryPostProcessor)然后在执行postProcessBeanFactory方法,然后从beanFactory获取BeanFactoryPostProcessor 然后执行postProcessBeanFactory,执行过程中都要按照PriorityOrdered,Ordered,普通的类型三种类型的顺序执行。

5、registerBeanPostProcessors:从beanFactory获取BeanPostProcessor分别按照PriorityOrdered,Ordered,普通的类型注册BeanPostProcessor BeanPostProcessor和BeanFactoryPostProcessor:前者是对bean初始化前后进行设置,后者可以对beanFactory进行修改 或者,可以对beanDefinition进行修改或者增加或者初始化渴望提前初始化的bean

6.initMessageSource():一般是我们用来初始化我们国际化文件的 7.initApplicationEventMulticaster():设置applicationEventMulticaster,spring发布各种事件就依靠他,这个和springboot发布事件使用相同的类 8.onRefresh():初始化其他的子容器类中的bean,同时创建spring的内置tomcat,这在后期Springboot内嵌式tomcat中详细阐述 8.registerListeners():添加用户设置applicationListeners,然后从beanFactory获取ApplicationListener,然后发布需要earlyApplicationEvents事件 9.finishBeanFactoryInitialization(beanFactory):实例化非懒加载的剩余bean 10.finishRefresh:清理资源缓存,初始化lifecycle,调用lifecycle的onrefresh,发布ContextRefreshedEvent的事件,激活JMX,启动tomcat

8、afterRefresh

this.afterRefresh(context, applicationArguments);目前是空的实现

9、发布started事件

listeners.started(context):发布started事件

10、执行ApplicationRunner和CommandLineRunner

this.callRunners(context, applicationArguments);

从spring容器中获取ApplicationRunner和CommandLineRunner对象,然后按照顺序排序,循环调用他们的run方法

handleRunFailure(context, ex, exceptionReporters, listeners)

11、发布监听器running事件

listeners.running(context):发布running事件

2. 发布ApplicationStartedEvent事件,如果想监听ApplicationStartedEvent事件,你可以这样定义:public class ApplicationStartedListener implements ApplicationListener,然后通过SpringApplication.addListener(..)添加进去即可。

3. 装配参数和环境,确定是web环境还是非web环境。

4. 装配完环境后,就触发ApplicationEnvironmentPreparedEvent事件。

5. 如果SpringApplication的showBanner属性被设置为true,则打印启动的Banner。

6. 创建ApplicationContext,会根据是否是web环境,来决定创建什么类型的ApplicationContext。

7. 装配Context的环境变量,注册Initializers、beanNameGenerator等。

8. 发布ApplicationPreparedEvent事件。

9. 注册springApplicationArguments、springBootBanner,加载资源等

10. 遍历调用所有SpringApplicationRunListener的contextLoaded()方法。

11. 调用ApplicationContext的refresh()方法,装配context beanfactory等非常重要的核心组件。

12. 查找当前ApplicationContext中是否注册有CommandLineRunner,如果有,则遍历执行它们。

13. 发布ApplicationReadyEvent事件,启动完毕,表示服务已经可以开始正常提供服务了。通常我们这里会监听这个事件来打印一些监控性质的日志,表示应用正常启动了。

SpringBoot会触发其他的一些事件,这些事件按下列顺序触发:

(1)ApplicationStartingEvent:项目刚启动时触发,此时除了注册监听器和初始器之外,其他所有处理都没有开始;

(2)ApplicationEnvironmentPreparedEvent:上下文得到环境信息之后触发,此时上下文创建还没有创建;

(3)ApplicationPreparedEvent:bean的定义信息加载完成之后触发,此时bean还没有初始化;

(4)ApplicationReadyEvent:在所有bean初始化完毕,所有回调处理完成,系统准备处理服务请求时触发;

(5)ApplicationFailedEvent:启动过程出现异常时候触发。

三、启动信息说明


启动信息如下:

1:启动SampleController。Starting SampleController on 2:查找active profile,无,设为default:No active profile set, falling back to default profiles: default 3: 刷新上下文:Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@12028586 4: 初始化tomcat,设置端口8080,设置访问方式为http:Tomcat initialized with port(s): 8080 (http) 5: 启动tomcat服务:Starting service Tomcat 6: 启动Servlet引擎:Starting Servlet Engine: Apache Tomcat/8.5.11 7: Spring内嵌的WebApplicationContext 初始化开始: Initializing Spring embedded WebApplicationContext 8: Spring内嵌的WebApplicationContext 初始化完成:Root WebApplicationContext: initialization completed in 4166 ms 9:映射servlet,将 dispatcherServlet 映射到 [/] 。: Mapping servlet: 'dispatcherServlet' to [/] 9: 映射filter,将 metricsFilter 映射到 [/*] 。 : Mapping filter: 'metricsFilter' to: [/*] 10:映射filter,将 characterEncodingFilter 映射到 [/*] 。 : Mapping filter: 'characterEncodingFilter' to: [/*] 11:映射filter,将 webRequestLoggingFilter 映射到 [/*] 。 : Mapping filter: 'webRequestLoggingFilter' to: [/*] 12:映射filter,将 CORSFilter 映射到 [/*] 。 : Mapping filter: 'CORSFilter' to: [/*] 13:映射filter,将 requestIdFilter 映射到 [/*] 。 : Mapping filter: 'requestIdFilter' to: [/*] 14:映射filter,将 applicationContextIdFilter 映射到 [/*] 。 : Mapping filter: 'applicationContextIdFilter' to: [/*] 15:tomcat启动完毕:Tomcat started on port(s): 8080 (http)

16: 初始化dispatcherServlet: Initializing Spring FrameworkServlet 'dispatcherServlet' 17 初始化dispatcherServlet完成å:FrameworkServlet 'dispatcherServlet': initialization completed in 20 ms

四、ApplicationContextInitializer、ApplicationContextAware、ApplicationContext、ApplicationListener的说明


ApplicationContextInitializer

用于在spring容器刷新之前初始化Spring ConfigurableApplicationContext的回调接口。(简单说就是在容器刷新之前调用该类的 initialize 方法。并将 ConfigurableApplicationContext 类的实例传递给该方法) 通常用于需要对应用程序上下文进行编程初始化的web应用程序中。例如,根据上下文环境注册属性源或激活配置文件等。 可排序的(实现Ordered接口,或者添加@Order注解)具体应用:https://guisu.blog.csdn.net/article/details/103885722

例如:

代码语言:javascript复制
​
@Service
public class Log2LoggerStarter implements ApplicationContextInitializer<ConfigurableApplicationContext> {

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        System.out.println("ApplicationContextInitializer start  ..................................");
        LoggerFactory.setLoggerCluzz(Log2Logger.class);
    }
}

​

ApplicationContextAware

当一个类实现了这个接口(ApplicationContextAware)之后,这个类就可以方便获得ApplicationContext中的所有bean。换句话说,就是这个类可以直接获取spring配置文件中,所有引用到的bean对象。

获取applicationContext:

代码语言:javascript复制
@Component
public class ApplicationContextHolder implements ApplicationContextAware {

    /**上下文对象实例*/
    private static ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(ApplicationContext appContext) throws BeansException {
        System.out.println("ApplicationContextInitializer start  ..................................");
        applicationContext = appContext;
    }

    /**
     * 获取applicationContext
     */
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    /**通过name获取 Bean.*/
    public static Object getBean(String name){
        return getApplicationContext().getBean(name);
    }

    /**通过class获取Bean.*/
    public static <T> T getBean(Class<T> clazz){
        return getApplicationContext().getBean(clazz);
    }

    /** 通过name,以及Clazz返回指定的Bean*/
    public static <T> T getBean(String name,Class<T> clazz){
        return getApplicationContext().getBean(name, clazz);
    }

}

ApplicationContext

ApplicationContext接口,它由BeanFactory接口派生而来,因而提供BeanFactory所有的功能。ApplicationContext以一种更向面向框架的方式工作以及对上下文进行分层和实现继承,ApplicationContext包还提供了以下的功能: • MessageSource, 提供国际化的消息访问 • 资源访问,如URL和文件 (统一的资源文件读取方式)

• 事件传播 ,有强大的事件机制(Event) • 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层

ApplicationListener

是一个接口,里面只有一个onApplicationEvent方法以自己的类在实现该接口的时候,要实装该方法。如果在上下文中部署一个实现了ApplicationListener接口的bean,那么每当在一个ApplicationEvent发布到 ApplicationContext时, 这个bean得到通知。其实这就是标准的Oberver设计模式。

代码语言:javascript复制
@Service
public class TraceLog2Initialize implements ApplicationListener<ContextRefreshedEvent> {
    private ApplicationContext applicationContext ;

    /**
     * <b>简述:</b> 启动时初始化
     *
     * @param event
     * @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent)
     */
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        System.out.println("ApplicationListener start  ...........................................");
        applicationContext = event.getApplicationContext();
    }
}

ApplicationEvent

是个抽象类,里面只有一个构造函数和一个长整型的timestamp。

我们可以通过上面启动分析,大概知道按ApplicationContextInitializer、ApplicationContextAware、ApplicationListener先后执行。具体实例看看这几个接口执行:

1)ApplicationContextInitializer最先执行,在初始化开始就执行:

2)ApplicationContextAware是在WebApplicationContext完成后执行。

3)最后是ApplicationListener

0 人点赞