美文网首页Spring cloud
SpringBoot2.0深度实践学习笔记(五)之 Spring

SpringBoot2.0深度实践学习笔记(五)之 Spring

作者: Sam_L | 来源:发表于2019-05-01 15:07 被阅读37次

    SpringApplication运行阶段

    • 加载:SpringApplication 运行监听器
    • 运行:SpringApplication 运行监听器
    • 监听:Spring Boot事件、Spring事件
    • 创建:应用上下文、Environment、其他
    • 失败:故障分析报告
    • 回调:CommandLineRunner、ApplicationRunner

    具体:

    (一)加载:SpringApplication 运行监听(SpringApplicationRunListeners)
    利用 Spring 工厂加载机制,读取 SpringApplicationRunListener 对象集合,并且封装到组合类
    SpringApplicationRunListeners

    public ConfigurableApplicationContext run(String... args) {
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            ConfigurableApplicationContext context = null;
            Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
            configureHeadlessProperty();
            SpringApplicationRunListeners listeners = getRunListeners(args);
            listeners.starting();
            try {
                ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                        args);
                ConfigurableEnvironment environment = prepareEnvironment(listeners,
                        applicationArguments);
                configureIgnoreBeanInfo(environment);
                Banner printedBanner = printBanner(environment);
                context = createApplicationContext();
                exceptionReporters = getSpringFactoriesInstances(
                        SpringBootExceptionReporter.class,
                        new Class[] { ConfigurableApplicationContext.class }, context);
                prepareContext(context, environment, listeners, applicationArguments,
                        printedBanner);
                refreshContext(context);
                afterRefresh(context, applicationArguments);
                stopWatch.stop();
                if (this.logStartupInfo) {
                    new StartupInfoLogger(this.mainApplicationClass)
                            .logStarted(getApplicationLog(), stopWatch);
                }
                listeners.started(context);
                callRunners(context, applicationArguments);
            }
            catch (Throwable ex) {
                handleRunFailure(context, ex, exceptionReporters, listeners);
                throw new IllegalStateException(ex);
            }
    
            try {
                listeners.running(context);
            }
            catch (Throwable ex) {
                handleRunFailure(context, ex, exceptionReporters, null);
                throw new IllegalStateException(ex);
            }
            return context;
        }
    

    说明:
    1)getRunListeners(args);//运行监听器
    可以看到工厂加载机制getSpringFactoriesInstances,把实例列表放进来了;
    同时SpringApplicationRunListeners
    这个对象在设计模式里称为组合对象
    当在实现某个接口的时候通过foreach的方式执行。
    SpringApplicationRunListeners里面有很多方法,都定义在SpringApplicationRunListener接口里

        private SpringApplicationRunListeners getRunListeners(String[] args) {
            Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
            return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
                    SpringApplicationRunListener.class, types, this, args));
        }
    
    
        SpringApplicationRunListeners(Log log,
                Collection<? extends SpringApplicationRunListener> listeners) {
            this.log = log;
            this.listeners = new ArrayList<>(listeners);
        }
    

    (二)运行:SpringApplication 运行监听器
    SpringApplicationRunListener 监听多个运行状态方法:

    • starting() : Spring 应用刚启动
    • environmentPrepared(ConfigurableEnvironment) : ConfigurableEnvironment 准备妥当,允许将其调整
    • contextPrepared(ConfigurableApplicationContext):ConfigurableApplicationContext 准备妥当,允许将其调整
    • contextLoaded(ConfigurableApplicationContext):ConfigurableApplicationContext 已装载,但仍未启动
    • started(ConfigurableApplicationContext): ConfigurableApplicationContext 已启动,此时 Spring Bean 已初始化完成
    • running(ConfigurableApplicationContext) :Spring 应用正在运行
    • failed(ConfigurableApplicationContext,Throwable) :Spring 应用运行失败

    (三)监听:Spring Boot事件、Spring事件
    Spring Boot 通过 SpringApplicationRunListener 的实现类 EventPublishingRunListener 利用 Spring Framework 事件
    API ,广播 Spring Boot 事件。

    spring.factories
    EventPublishingRunListener是SpringApplicationRunListener唯一实现
    它把接口都实现了一遍,从EventPublishingRunListener源码中看到都是和事件相关联

    # Run Listeners
    org.springframework.boot.SpringApplicationRunListener=\
    org.springframework.boot.context.event.EventPublishingRunListener
    

    EventPublishingRunListener监听方法与SpringBoot事件对应关系

    • starting() :ApplicationStartingEvent 1.5
    • environmentPrepared(ConfigurableEnvironment):ApplicationEnvironmentPreparedEvent 1.0
    • contextPrepared(ConfigurableApplicationContext)
    • contextLoaded(ConfigurableApplicationContext): ApplicationPreparedEvent 1.0
    • started(ConfigurableApplicationContext) ApplicationStartedEvent 2.0
    • running(ConfigurableApplicationContext): ApplicationReadyEvent 2.0
    • failed(ConfigurableApplicationContext,Throwable) :ApplicationFailedEvent 1.0

    EventPublishingRunListener源码
    有一个Multicaster广播器,会广播一个事件multicastEvent。
    每个方法调用都会广播不同的事件如ApplicationStartingEvent、ApplicationEnvironmentPreparedEvent、ApplicationContextInitializedEvent、ApplicationPreparedEvent、ApplicationStartedEvent、ApplicationReadyEvent

    @Override
        public void starting() {
            this.initialMulticaster.multicastEvent(
                    new ApplicationStartingEvent(this.application, this.args));
        }
    
    @Override
        public void environmentPrepared(ConfigurableEnvironment environment) {
            this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
                    this.application, this.args, environment));
        }
    
        @Override
        public void contextPrepared(ConfigurableApplicationContext context) {
            this.initialMulticaster.multicastEvent(new ApplicationContextInitializedEvent(
                    this.application, this.args, context));
        }
    ……
    

    SimpleApplicationEventMulticaster:简单事件应用广播器,这个广播器在不同的回调方法里做广播.

        public EventPublishingRunListener(SpringApplication application, String[] args) {
            this.application = application;
            this.args = args;
            this.initialMulticaster = new SimpleApplicationEventMulticaster();
            for (ApplicationListener<?> listener : application.getListeners()) {
                this.initialMulticaster.addApplicationListener(listener);
            }
        }
    
    这些事件是Spring Framework 的事件扩展。
    
    监听Spring事件
    

    1、Spring Framework 事件/监听器编程模型

    • Spring 应用事件
      普通应用事件: ApplicationEvent 基类
      应用上下文事件: ApplicationContextEvent
    • Spring 应用监听器
      接口编程模型: ApplicationListener
      注解编程模型: @EventListener
    • Spring 应用事广播器
      接口: ApplicationEventMulticaster
      实现类: SimpleApplicationEventMulticaster
      执行模式:同步或异步

    说明:
    同步或异步
    SimpleApplicationEventMulticaster 里面定义了taskExecutor(并发的时候,线程池的执行器)
    在广播事件的时候multicastEvent,当线程池存在的时候,是异步去执行的

    public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
    
        @Nullable
        private Executor taskExecutor;
    
        @Nullable
        private ErrorHandler errorHandler;
    
    ……
    
    public void setTaskExecutor(@Nullable Executor taskExecutor) {
            this.taskExecutor = taskExecutor;
        }
    
    ……
    
    @Override
        public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
            ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
            for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
                Executor executor = getTaskExecutor();
                if (executor != null) {
                    executor.execute(() -> invokeListener(listener, event));
                }
                else {
                    invokeListener(listener, event);
                }
            }
        }
    

    【栗子1----Spring Framework 事件/监听器】
    在spring-application模块下新建一个SpringApplicationEventBootstrap类
    (采用SpringFrameWork实现)

    /**
     * spring 应用事件引导类
     */
    public class SpringApplicationEventBootstrap类 {
    
        public static void main(String[] args) {
            //创建上下文(注解驱动)
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    
            //注册应用事件监听器
            context.addApplicationListener(event ->
                    System.out.println("监听到事件:" + event));
    
            //启动上下文
            context.refresh();
    
            //发送事件
            context.publishEvent("HelloWorld");
            context.publishEvent("2019");
    
            context.publishEvent(new ApplicationEvent("PK") {
            });
    
    
            //关闭上下文
            context.close();
    
        }
    }
    

    运行


    1.png

    说明:
    1)publishEvent---这个接口是发了一个PayloadApplicationEvent事件

    2)可以自定义事件
    3)springboot实现也是类似的

    【栗子2---实现SpringApplication运行监听器】
    SpringApplicationRunListener是通过工厂机制加载的
    (1)新建一个包run下面新建一个类HelloWorldRunListener,实现接口SpringApplicationRunListener,简单的实现第一个starting()

    /**
     * HelloWorld {@link SpringApplicationRunListener}
     */
    public class HelloWorldRunListener implements SpringApplicationRunListener {
    
        public HelloWorldRunListener(SpringApplication application, String[] args){
            
        }
        @Override
        public void starting() {
            System.out.println("HelloWorldRunListener.starting()...");
        }
    
        @Override
        public void environmentPrepared(ConfigurableEnvironment environment) {
    
        }
    
        @Override
        public void contextPrepared(ConfigurableApplicationContext context) {
    
        }
    
        @Override
        public void contextLoaded(ConfigurableApplicationContext context) {
    
        }
    
        @Override
        public void started(ConfigurableApplicationContext context) {
    
        }
    
        @Override
        public void running(ConfigurableApplicationContext context) {
    
        }
    
        @Override
        public void failed(ConfigurableApplicationContext context, Throwable exception) {
    
        }
    }
    
    

    注意:
    EventPublishingRunListener有2个参数(SpringApplication application, String[] args)
    如果你没有添加这2个参数,会报错:构造器没有这个方法:Caused by: java.lang.NoSuchMethodException: com.cbt.diveinspringboot.run.HelloWorldRunListener.<init>
    我们看一下源码:
    EventPublishingRunListener

        public EventPublishingRunListener(SpringApplication application, String[] args) {
            this.application = application;
            this.args = args;
            this.initialMulticaster = new SimpleApplicationEventMulticaster();
            for (ApplicationListener<?> listener : application.getListeners()) {
                this.initialMulticaster.addApplicationListener(listener);
            }
        }
    

    在SpringApplication.java 里面传了二个参数this, args

    private SpringApplicationRunListeners getRunListeners(String[] args) {
            Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
            return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
                    SpringApplicationRunListener.class, types, this, args));
        }
    

    getSpringFactoriesInstances,把所有的class都加载起来了,但是初始化的时候是根据构造器初始化的,同时将构造器里面的2个参数传进来

        private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
                Class<?>[] parameterTypes, Object... args) {
            ClassLoader classLoader = getClassLoader();
            // Use names and ensure unique to protect against duplicates
            Set<String> names = new LinkedHashSet<>(
                    SpringFactoriesLoader.loadFactoryNames(type, classLoader));
            List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
                    classLoader, args, names);
            AnnotationAwareOrderComparator.sort(instances);
            return instances;
        }
    
    
    private <T> List<T> createSpringFactoriesInstances(Class<T> type,
                Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
                Set<String> names) {
            List<T> instances = new ArrayList<>(names.size());
            for (String name : names) {
                try {
                    Class<?> instanceClass = ClassUtils.forName(name, classLoader);
                    Assert.isAssignable(type, instanceClass);
                    Constructor<?> constructor = instanceClass
                            .getDeclaredConstructor(parameterTypes);
                    T instance = (T) BeanUtils.instantiateClass(constructor, args);
                    instances.add(instance);
                }
                catch (Throwable ex) {
                    throw new IllegalArgumentException(
                            "Cannot instantiate " + type + " : " + name, ex);
                }
            }
            return instances;
        }
    

    (2) 配置spring.factories

    # SpringApplicationRunListener实现配置
    org.springframework.boot.SpringApplicationRunListener=\
    com.cbt.diveinspringboot.run.HelloWorldRunListener
    

    (3)运行之前的引导类SpringApplicationBootstrap
    在第一行就输出了


    3.png

    2、监听SpirngBoot事件
    之前看EventPublishingRunListener类源码的时候,它是通过SpringApplicationRunListener回调的方式,发生不同的事件,产生不同效应。

    在SpringApplication准备阶段的时候要加载ApplicationListener。
    在本工程里spring.factories里面我们已经配置了相关Listener的实现。在构造阶段也已经将Listener装配好了。

    当事件发送的时候会把application关联的listerner传进来,通过迭代的方式逐一去执行监听

        public EventPublishingRunListener(SpringApplication application, String[] args) {
            this.application = application;
            this.args = args;
            this.initialMulticaster = new SimpleApplicationEventMulticaster();
            for (ApplicationListener<?> listener : application.getListeners()) {
                this.initialMulticaster.addApplicationListener(listener);
            }
        }
    

    再看一下SimpleApplicationEventMulticaster
    实现里的发布事件的时候会迭代的完成。
    getApplicationListeners会对事件加以区分

        @Override
        public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
            ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
            for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
                Executor executor = getTaskExecutor();
                if (executor != null) {
                    executor.execute(() -> invokeListener(listener, event));
                }
                else {
                    invokeListener(listener, event);
                }
            }
        }
    

    当我们不知道实现或者配置在spring工厂加载机制哪里实现的时候,借助IDEA工具find usages,看到非代码的配置(Non-code usages)

    # Application Listeners
    org.springframework.context.ApplicationListener=\
    org.springframework.boot.ClearCachesApplicationListener,\
    org.springframework.boot.builder.ParentContextCloserApplicationListener,\
    org.springframework.boot.context.FileEncodingApplicationListener,\
    org.springframework.boot.context.config.AnsiOutputApplicationListener,\
    org.springframework.boot.context.config.ConfigFileApplicationListener,\
    org.springframework.boot.context.config.DelegatingApplicationListener,\
    org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
    org.springframework.boot.context.logging.LoggingApplicationListener,\
    org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
    

    点击FileEncodingApplicationListener、LoggingApplicationListener上涉及的等等,可以看到他们关心的相关事件,以及顺序。

    以上就是Spring Boot事件或者Spring事件应用的情况
    特别要注意:监听事件是哪种事件以及顺序。

    【栗子----监听SpringBoot事件】
    先看一下ConfigFileApplicationListener源码

    public class ConfigFileApplicationListener
            implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {
    public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 10;
    ……
    private int order = DEFAULT_ORDER;
    ……
    @Override
        public void onApplicationEvent(ApplicationEvent event) {
            if (event instanceof ApplicationEnvironmentPreparedEvent) {
                onApplicationEnvironmentPreparedEvent(
                        (ApplicationEnvironmentPreparedEvent) event);
            }
            if (event instanceof ApplicationPreparedEvent) {
                onApplicationPreparedEvent(event);
            }
        }
    ……
    }
    

    说明:
    1)ConfigFileApplicationListener关心2个事件,ApplicationEnvironmentPreparedEvent和ApplicationPreparedEvent,事件的执行顺序是ApplicationEnvironmentPreparedEvent是在ApplicationPreparedEvent前面的。

    2)ApplicationEnvironmentPreparedEvent会读取application.properties文件。并且顺序要在DEFAULT_ORDER之前,也就是HIGHEST_PRECEDENCE + 10;(表示如果最大优先级第一个执行,它就是第11个执行,如果要比它优先的话+9)

    具体操作:
    (1)配置application.properties文件

    name=PK
    

    (2)在listener包下新建一个类BeforeConfigFileApplicationListener

    /**
     * Before {@link ConfigFileApplicationListener} 实现
     */
    public class BeforeConfigFileApplicationListener implements SmartApplicationListener,Ordered {
    
        @Override
        public int getOrder() {
            //比ConfigFileApplicationListener优先级更高
            return ConfigFileApplicationListener.DEFAULT_ORDER +1;
        }
    
        @Override
        public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
            return ApplicationEnvironmentPreparedEvent.class.isAssignableFrom(eventType)
                    || ApplicationPreparedEvent.class.isAssignableFrom(eventType);
        }
    
        @Override
        public boolean supportsSourceType(Class<?> sourceType) {
            return true;
        }
    
    
        @Override
        public void onApplicationEvent(ApplicationEvent event) {
            if (event instanceof ApplicationEnvironmentPreparedEvent) {
    
                ApplicationEnvironmentPreparedEvent preparedEvent = (ApplicationEnvironmentPreparedEvent) event;
               Environment environment =  preparedEvent.getEnvironment();
                System.out.println("environment.getProperty(\"name\") :" +environment.getProperty("name"));
    
            }
            if (event instanceof ApplicationPreparedEvent) {
    
            }
        }
    
    

    说明:
    1)实现接口Ordered和SmartApplicationListener
    2)执行顺序,这里先采用比源码执行顺序靠前的顺序,所以运行的时候application.properties里的name是读不到的。
    3)实现SmartApplicationListener接口的方法参照SmartApplicationListener的源码,进行修改。这里不处理2个事件了,通过PreparedEvent事件可以读到Environment来关联属性。

    (3)将内容配置到spring.factories里(最后一个)

    # Application Listeners 实现配置
    org.springframework.context.ApplicationListener=\
    com.cbt.diveinspringboot.listener.AfterHelloWorldApplicationListener,\
    com.cbt.diveinspringboot.listener.HelloWorldApplicationListener,\
    com.cbt.diveinspringboot.listener.BeforeConfigFileApplicationListener
    

    (4)运行SpringApplicationBootstrap
    name为null


    4.png

    当把执行顺序改为+1,在其之后执行
    name就读取到了

    5.png

    要特别了解springboot的特性,优先级顺序

    (四)创建:应用上下文( ConfigurableApplicationContext )
    根据准备阶段的推断 Web 应用类型创建对应的 ConfigurableApplicationContext 实例:

    • Web Reactive: AnnotationConfigReactiveWebServerApplicationContext
    • Web Servlet: AnnotationConfigServletWebServerApplicationContext
    • 非 Web: AnnotationConfigApplicationContext

    创建 Environment
    根据准备阶段的推断 Web 应用类型创建对应的 ConfigurableEnvironment 实例:

    • Web Reactive: StandardEnvironment
    • Web Servlet: StandardServletEnvironment
    • 非 Web: StandardEnvironment

    在SpringApplication.run方法里有个createApplicationContext()
    根据推断web类型,如果是
    根据推断web类型,如果是
    SERVLET---DEFAULT_SERVLET_WEB_CONTEXT_CLASS
    REACTIVE-----DEFAULT_REACTIVE_WEB_CONTEXT_CLASS
    default----DEFAULT_CONTEXT_CLASS

    protected ConfigurableApplicationContext createApplicationContext() {
            Class<?> contextClass = this.applicationContextClass;
            if (contextClass == null) {
                try {
                    switch (this.webApplicationType) {
                    case SERVLET:
                        contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
                        break;
                    case REACTIVE:
                        contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                        break;
                    default:
                        contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
                    }
                }
                catch (ClassNotFoundException ex) {
                    throw new IllegalStateException(
                            "Unable create a default ApplicationContext, "
                                    + "please specify an ApplicationContextClass",
                            ex);
                }
            }
            return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
        }
    

    SpringApplication.java中的getOrCreateEnvironment()
    当 SERVLET的时候就是StandardServletEnvironment,其他的时候就是StandardEnvironment。

        private ConfigurableEnvironment getOrCreateEnvironment() {
            if (this.environment != null) {
                return this.environment;
            }
            switch (this.webApplicationType) {
            case SERVLET:
                return new StandardServletEnvironment();
            case REACTIVE:
                return new StandardReactiveWebEnvironment();
            default:
                return new StandardEnvironment();
            }
        }
    

    在SpringApplication的run方法中也间接的表达了

        private ConfigurableEnvironment getOrCreateEnvironment() {
            if (this.environment != null) {
                return this.environment;
            }
            switch (this.webApplicationType) {
            case SERVLET:
                return new StandardServletEnvironment();
            case REACTIVE:
                return new StandardReactiveWebEnvironment();
            default:
                return new StandardEnvironment();
            }
        }
    

    再看run方法源码:
    在starting的时候什么都没创建,后续开始创建一些,当Environment创建好了,会执行另外一个listener事件(environmentPrepared(environment)),那么ConfigurableEnvironment准备妥当,允许将其调整(参看之前运行SpringApplication运行监听器的监听方法)

    然后会遇到createApplicationContext
    然后contextPrepared调用就开始了
    然后contextLoaded(context)事件开始了

    等等,这些都是通过SpringApplication 上下文进行逐一调用的,伴随事件的产生,把相应的资源准备好

    我们可以调整这个类型
    【栗子】
    模块下新建一个类SpringApplicationContextBootstrap

    /**
     * Spring应用上下文引导类
     */
    @SpringBootApplication
    public class SpringApplicationContextBootstrap {
    
        public static void main(String[] args) {
    
            ConfigurableApplicationContext context =  new SpringApplicationBuilder(SpringApplicationContextBootstrap.class)
                    //.web(WebApplicationType.NONE) //非web类型
                    .run(args);
    
            System.out.println("ConfigurableApplicationContext 类型:" +context.getClass().getName());
     System.out.println("Environment 类型:" +context.getEnvironment().getClass().getName());
    
            context.close();
        }
    }
    

    运行:
    1)推断出servlet,并且查看Environment类型的时候:

    6.png

    2)非web类型的话显示,并且查看Environment类型的时候:就是StandardEnvironment


    8.png

    3)换成REACTIVE会报错:没有jar包


    终于完成了关于SpringApplication的相关学习,里面的东西还是需要结合源码再深度理解,把所有的东西串起来一下。

    相关文章

      网友评论

        本文标题:SpringBoot2.0深度实践学习笔记(五)之 Spring

        本文链接:https://www.haomeiwen.com/subject/cdfunqtx.html