Spring Boot 2.0深度实践之核心技术篇【第三章 理解 SpringApplication】

3-1 理解 SpringApplication

在这里插入图片描述

3-2 基础技术和衍生技术

在这里插入图片描述
在这里插入图片描述

3-3 合并工程

新建module

3-4 SpringApplication 准备阶段

在这里插入图片描述

SpringApplication 运行
SpringApplication.run(DiveInSpringBootApplication.class, args)
自定义 SpringApplication
  • 通过 SpringApplication API调整
SpringApplication springApplication = new 
SpringApplication(DiveInSpringBootApplication.class); 
springApplication.setBannerMode(Banner.Mode.CONSOLE); 
springApplication.setWebApplicationType(WebApplicationType.NONE); 
springApplication.setAdditionalProfiles("prod"); 
springApplication.setHeadless(true);
  • 通过 SpringApplicationBuilder API调整
new SpringApplicationBuilder(DiveInSpringBootApplication.class) 
   .bannerMode(Banner.Mode.CONSOLE)    
   .web(WebApplicationType.NONE)    
   .profiles("prod")    
   .headless(true)    
   .run(args);

在这里插入图片描述

配置 Spring Boot Bean 源

Java 配置 Class 或 XML 上下文配置文件集合,用于 Spring Boot BeanDefinitionLoader 读取 ,并且将配置源解析加载为 Spring Bean 定义。
数量:一个或多个以上。

Java 配置 Class

用于 Spring 注解驱动中 Java 配置类,大多数情况是 Spring 模式注解所标注的类,如 @Configuration

XML 上下文配置文件

用于 Spring传统配置驱动中的 XML 文件。

推断 Web 应用类型

根据当前应用 ClassPath 中是否存在相关实现类来推断 Web 应用的类型,包括:

    Web Reactive: WebApplicationType.REACTIVE 
    Web Servlet: WebApplicationType.SERVLET 
    非 Web: WebApplicationType.NONE

参考方法:
org.springframework.boot.SpringApplication#deduceWebApplicationType

 private WebApplicationType deduceWebApplicationType() { 
        if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null) && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {   
                 return WebApplicationType.REACTIVE;        
        }        
        for (String className : WEB_ENVIRONMENT_CLASSES) {
            if (!ClassUtils.isPresent(className, null)) {
                return WebApplicationType.NONE;            
            }        
        }        
        return WebApplicationType.SERVLET;    
    }

3-7 推断引导类

根据 Main 线程执行堆栈判断实际的引导类

参考方法: org.springframework.boot.SpringApplication#deduceMainApplicationClass

  private Class<?> deduceMainApplicationClass() {        
      try {
          StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
          for (StackTraceElement stackTraceElement : stackTrace) {
              if ("main".equals(stackTraceElement.getMethodName())) {
                  return Class.forName(stackTraceElement.getClassName());
              }            
          }        
      }catch (ClassNotFoundException ex) {
            // Swallow and continue        
      }        
      return null;    
  }

3-8 加载应用上下文初始器

加载应用上下文初始器 ( ApplicationContextInitializer)

利用 Spring工厂加载机制,实例化 ApplicationContextInitializer实现类,并排序对象集合。

  • 实现
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,    
  Class<?>[] parameterTypes, Object... args) {
  
     ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
     
     // 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; 
}
  • 技术
    • 实现类: org.springframework.core.io.support.SpringFactoriesLoader
    • 配置资源: META-INF/spring.factories
    • 排序: AnnotationAwareOrderComparator#sort
实践
spring.factories
# Initializers
org.springframework.context.ApplicationContextInitializer=\
com.example.test3_8.AfterHelloApplicationContextInitializer,\
com.example.test3_8.HelloApplicationContextInitializer
Application3_8.java
@SpringBootApplication
public class Application3_8 {
    public static void main(String[] args) {

        new SpringApplicationBuilder(Application.class).run(args);
    }

}
AfterHelloApplicationContextInitializer.java
@Order
public class AfterHelloApplicationContextInitializer implements ApplicationContextInitializer {
    @Override
    public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
        System.out.println("AfterHelloApplicationContextInitializer\t" + configurableApplicationContext.getId());
    }
}
HelloApplicationContextInitializer.java
@Order(Ordered.HIGHEST_PRECEDENCE)
public class HelloApplicationContextInitializer implements ApplicationContextInitializer {
    @Override
    public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
        System.out.println("HelloApplicationContextInitializer\t" + configurableApplicationContext.getId());
    }
}

输出结果

在这里插入图片描述

3-9 加载应用事件监听器

加载应用事件监听器( ApplicationListener

利用 Spring工厂加载机制,实例化 ApplicationListener实现类,并排序对象集合

spring.factories
# Initializers
org.springframework.context.ApplicationContextInitializer=\
com.example.test3_8.AfterHelloApplicationContextInitializer,\
com.example.test3_8.HelloApplicationContextInitializer
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.test2_9.HelloWorldAutoConfiguration2_9
# Application Listeners
org.springframework.context.ApplicationListener=\
com.example.test3_9.HelloApplicationListener

HelloApplicationListener.java

import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;

@Order(Ordered.HIGHEST_PRECEDENCE)
public class HelloApplicationListener implements ApplicationListener<ContextRefreshedEvent> {

    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        System.out.println("HelloApplicationListener\t" + contextRefreshedEvent.getApplicationContext().getId()
                + "\ttime\t" + contextRefreshedEvent.getTimestamp());
    }
}

3-10 SpringApplication 运行阶段

在这里插入图片描述
在这里插入图片描述

加载 SpringApplication运行监听器( SpringApplicationRunListeners

利用 Spring工厂加载机制,读取 SpringApplicationRunListener对象集合,并且封装到组合类 SpringApplicationRunListeners

运行 SpringApplication运行监听器( SpringApplicationRunListeners

SpringApplicationRunListener监听多个运行状态方法

监听方法阶段说明Spring Boot 起始版本
starting()Spring 应用刚启动1.0
environmentPrepared(ConfigurableEnvironment)ConfigurableEnvironment 准备妥当,允许将其调整1.0
contextPrepared(ConfigurableApplicationContext)ConfigurableApplicationContext 准备妥当,允许将其调整1.0
contextLoaded(ConfigurableApplicationContext)ConfigurableApplicationContext 已装载,但仍未启动1.0
started(ConfigurableApplicationContext)ConfigurableApplicationContext 已启动,此时 Spring Bean 已初始化完成2.0
running(ConfigurableApplicationContext)Spring 应用正在运行2.0
failed(ConfigurableApplicationContext,Throwable)Spring 应用运行失败2.0

3-11 SpringApplication 运行监听器事件监听器编程模型

监听 Spring Boot 事件 / Spring 事件

Spring Boot通过 SpringApplicationRunListener的实现类 EventPublishingRunListener利用 Spring Framework事件 API,广播 Spring Boot事件

Spring Framework 事件/监听器编程模型
  • Spring 应用事件

    • 普通应用事件: ApplicationEvent
    • 应用上下文事件: ApplicationContextEvent
  • Spring 应用监听器

    • 接口编程模型: ApplicationListener
    • 注解编程模型: @EventListener
  • Spring 应用事广播器

    • 接口: ApplicationEventMulticaster
    • 实现类: SimpleApplicationEventMulticaster
      • 执行模式:同步或异步

    HelloBootstrapApplication3_11.java

public class HelloBootstrapApplication3_11 {
   public static void main(String[] args) {

       AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

       context.addApplicationListener( e->{
           System.out.println("监听到事件:\t" + e.getClass());
       });

       //启动以用上下文
       context.refresh();
       context.publishEvent("hey man!");

       context.publishEvent(new ApplicationEvent("hi,my girl"){});

       //关闭应用上下文
       context.close();
   }
}

注意:如果出现以下错误:

Exception in thread "main" java.lang.IllegalStateException: ApplicationEventMulticaster not initialized - call 'refresh' before multicasting events via the context: org.springframework.context.annotation.AnnotationConfigApplicationContext@2a33fae0, started on Thu Jan 01 08:00:00 CST 1970
at org.springframework.context.support.AbstractApplicationContext.getApplicationEventMulticaster(AbstractApplicationContext.java:424)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:403)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:373)
at com.example.test3_11.HelloBootstrapApplication3_11.main(HelloBootstrapApplication3_11.java:13)

问题分析:

应用尚未启动,不能发布事件

解决办法:

 先启动应用再发布事件。

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

监听方法Spring Boot 事件Spring Boot 起始版本
starting()ApplicationStartingEvent1.5
environmentPrepared(ConfigurableEnvironment)ApplicationEnvironmentPreparedEvent1.0
contextPrepared(ConfigurableApplicationContext)contextLoaded(ConfigurableApplicationContext) ApplicationPreparedEvent1.0
started(ConfigurableApplicationContext)ApplicationStartedEvent2.0
running(ConfigurableApplicationContext)ApplicationReadyEvent2.0
failed(ConfigurableApplicationContext,Throwable)ApplicationFailedEvent1.0

3-13 监听 Spring Boot 事件

application.properties
name=whaleson
ConfigFileApplicationListener3_13.java
public class ConfigFileApplicationListener3_13 implements Ordered, SmartApplicationListener {
    @Override
    public boolean supportsEventType(Class<? extends ApplicationEvent> event) {


        return true;
    }

    @Override
    public int getOrder() {
        return ConfigFileApplicationListener.DEFAULT_ORDER + 1;
    }

    @Override
    public void onApplicationEvent(ApplicationEvent event) {

        if (event instanceof ApplicationEnvironmentPreparedEvent) {
            ApplicationEnvironmentPreparedEvent config = (ApplicationEnvironmentPreparedEvent)event;

            String name  = config.getEnvironment().getProperty("name");

            System.out.println("Your name is:\t" + name);
        }

        if (event instanceof ApplicationPreparedEvent) {
        }
    }
}
spring.factories
org.springframework.context.ApplicationListener=\
com.example.test3_13.ConfigFileApplicationListener3_13
说明

ConfigFileApplicationListener是用于加载application.propertiesapplication.yml文件。当自定义Listener读取配置文件的信息时,优先级不超过ConfigFileApplicationListener.DEFAULT_ORDER

3-14 创建 Spring 应用上下文

创建 Spring应用上下文( ConfigurableApplicationContext

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

  • Web Reactive: AnnotationConfigReactiveWebServerApplicationContext
  • Web Servlet: AnnotationConfigServletWebServerApplicationContext
  • 非 Web: AnnotationConfigApplicationContext
创建 Environment

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

  • Web Reactive: StandardEnvironment
  • Web Servlet: StandardServletEnvironment
  • 非 Web: StandardEnvironment
SpringApplication.java
protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
                switch(this.webApplicationType) {
                case SERVLET:
                    contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
                    break;
                case REACTIVE:
                    contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
                    break;
                default:
                    contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
                }
            } catch (ClassNotFoundException var3) {
                throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
            }
        }

        return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
    }
说明: 根据web类型创建不同类型的applicationContext。
Application3_14.java
非web
public class Application3_14 {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = new SpringApplicationBuilder(Application3_14.class)
                .web(WebApplicationType.NONE)
                .run(args);
        System.out.println("ConfigurableApplicationContext application type:\t" + context.getClass());
        System.out.println("Environment type:\t" + context.getEnvironment().getClass().getName());
    }
}
输出:
ConfigurableApplicationContext application type:	class org.springframework.context.annotation.AnnotationConfigApplicationContext
Environment type:	org.springframework.core.env.StandardEnvironment
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值