SpringApplication是SpringBoot提供的一个工具类,它提供了run()方法来启动Spring容器,运行SpringBoot应用。
类配置与XML配置
传统Spring大多使用XML进行配置,但SpringBoot使用java类(带@Configuration注解)进行配置。
如果希望SpringBoot能加载其他配置类或扫描其他包下的配置类或bean组件可使用如下注解:
@Import:显式指定要加载的配置类
@ComponentScan:指定SpringBoot扫描指定包及其子包下所有的配置类或bean组件。
@ImportResource:导入XML配置。- // 额外指定扫描org.crazyit.app和org.fkit.app包及其子包下所有配置类和Bean组件
- @SpringBootApplication(scanBasePackages = {"org.crazyit.app", "org.fkit.app"})
- // 加载类加载路径下beans.xml文件作为配置文件
- @ImportResource("classpath:beans.xml")
- // 加载cn.fkjava.app包下的MyConfig文件作为配置类
- @Import(cn.fkjava.app.MyConfig.class)
- public class App
- {
- public static void main(String[] args)
- {
- // 创建Spring容器、运行Spring Boot应用
- SpringApplication.run(App.class, args);
- }
- }
复制代码 启动日志与失败分析器
使用SpringApplication的run方法启动应用,默认显式info级别的日志,如果想关闭启动信息日志,则可设置为
spring.main.log-startup-info=false
上面的配置也会关闭活动Profile日志
将日志级别设为debug
java -jar firstboot-0.0.1-snapshot.jar --debug // jar包启动时设置
在application.properties添加配置- logging.level.org.springframework.boot.autoconfigure.logging=debug
复制代码 虽然SpringBoot内置大量的失败分析器,但依然允许开发者注册自己的失败分析器,以下是开发自定义的失败分析器- // 自定义失败分析器应继承AbstractFailureAnalyzer<T> ,泛型T代表失败分析器要处理的异常
- public class MyAnalyzer extends AbstractFailureAnalyzer<BindException>
- {
- // 重写analyze()方法,分析异常
- // 返回值FailureAnalysis代表分析结果,如果不想分析异常,可以返回null
- // 返回值FailureAnalysis的本质上是一个异常分析结果的描述信息,包含三个信息
- // 1. 错误信息(错误的原因,第一个参数)
- // 2. 修复建议(如何解决错误,第二个参数)
- // 3. 导致失败的异常(导致错误的异常,第三个参数)
- @Override
- public FailureAnalysis analyze(Throwable rootFailure, BindException cause)
- {
- cause.printStackTrace();
- return new FailureAnalysis("程序启动出错,程序绑定的端口被占用:"
- + cause.getMessage(),
- "请先停止占用8080端口的程序后再运行本应用或使用" +
- "server.port改变本应用的端口", cause);
- }
- }
复制代码 自定义失败分析器需要在项目的resources/META-INF/spring.factories文件中注册,内容如下:- org.springframework.boot.diagnostics.FailureAnalyzer=\
- org.crazyit.app.MyAnalyzer
复制代码 延迟初始化
SpringBoot允许将ApplicationContext的预初始化改为延迟初始化,有一下三种方式:
- 调用SpringApplicationBuilder.lazyInitialization(true)
- 调用SpringApplication.setLazyInitialization(true);
- 在application.properties配置spring.main.lazy-initialization=true
启用延迟初始化后,spring容器不会预初始化bean,而是等到需要调用bean的方法时才执行初始化,因此可降低SpringBoot的启动时间,但是有如下缺点:
- 在web应用中,web相关的bean要等到http请求第一次到来时才会初始化,因为会降低http的响应效率。
- bean错误被延迟发现。如果bean的配置存在错误,启动时不会报错,等到应用这个bean时才会报错,这样就延迟了发现错误的时机。
- 运行过程中内存紧张。随着程序运行,当应用中所有bean初始化完成后,可能会造成运行时内存紧张。
一般来说,除非特殊需要,不建议启动延迟初始化。
自定义banner
在启动springboot时,默认显示Spring的banner,可在application.properties中设置
- spring.main.banner-mode=console // 在控制台输出banner
复制代码- spring.main.banner-mode=log // 在日志文件中输出banner
复制代码- spring.main.banner-mode=off // 关闭banner
复制代码 SpringBoot允许自定义banner,在类加载路径下加一个banner.txt或设置spring.banner.location,可指定自定义banner文件位置;默认使用UTF-8字符集来读取banner内容,如果需要改变字符集,可在application.properties配置:- spring.banner.charset=GBK
复制代码 SpringBoot允许使用图片文件在夹banner,在类加载路径下加一个banner.gif(gif\jpg\png)或设置spring.banner.image.location;SpringBoot会把图片转换成字符画(ASCII art)形式。- # 定义图片banner的大小
- spring.banner.image.height=20
- spring.banner.image.width=60
- # 设置字符串色深
- spring.banner.image.bitdepth=4
复制代码 当图片banner和文本banner都存在,会先显示图片banner,再显示文本banner。
SpringApplication还提供setBanner()设置Banner。
设置SpringApplication与流式API
如果要对SpringApplication进行自定义设置,需要先定义SpringApplication对象,然后调用run()。- public static void main(String[] args) {
- var application = new SpringApplication(App.class);
- application.setLazyInitialization(true); // 设置懒加载
- application.setBanner(new Banner() {
- @Override
- public void printBanner(Environment environment, Class<?> sourceClass, PrintStream out) {
- out.println("===================================");
- out.println(" 欢迎使用Spring Boot ");
- out.println("===================================");
- }
- });
- // 创建Spring容器、运行Spring Boot应用
- application.run(args);
- }
复制代码 除了直接使用构造器来创建SpringApplication对象,也可以使用SpringApplicationBuilder工具类以流式API来创建SpringApplication- new SpringApplicationBuilder()
- // 设置应用的主类(父容器)
- .sources(Parent.class)
- // 设置应用的子类 (子容器)
- .child(App.class)
- .setlazyInitialization(true) // 设置懒加载
- .bannerMode(Banner.Mode.OFF) // 关闭Banner
- .run(args);
复制代码 让多个Spring容器有层次结构具有很多好处,比如,由于子容器是由父容器负责加载的,因此子容器中的bean可以访问父容器中的bean,但父容器中的bean不能访问子容器中的bean。
事件监听器和容器初始化器
SpringApplication注册事件监听器的方式
- 调用SpringApplication的addLIsteners()或者SpringApplicationBuilder的listeners()来添加事件监听器。
- 在/META-INF/spring.factories文件中注册事件监听器
- org.springframework.context.ApplicationListener=org.crazyit.app.MyListener
复制代码 当SpringBoot启动时,SpringApplication会依次触发如下事件:
- 应用刚刚开始,未进行任何处理,触发ApplicationStartingEvent事件,除非监听器和初始化器注册失败。
- 当ApplicationContext要使用的Environment已经确定,但ApplicationContext还未被创建时,触发ApplicationEnvironmentPreparedEvent事件。
- 当ApplicationContext准备完成且初始化器已被调用,但未加载任何bean定义前,触发ApplicationContextInitializedEvent事件。
- 在所有bean定义被加载之后,Spring容器刷新之前,触发ApplicationPreparedEvent事件。
- 在Spring容器刷新之后,ApplicationRunner、CommandLineRunner的接口方法被调用之前,触发ApplicationStartedEvent事件。
- 当应用程序LivenessState状态变成CORRECT,表明应用程序进入live状态,触发AvailabilityChangeEvent事件。
- 当所有ApplicationRunner、CommandLineRunner的接口方法被调用完成后,触发ApplicationReadyEvent事件。
- 当应用程序的ReadinessState状态变成ACCEPTING_TRAFFIC,表明应用程序准备接受服务请求,触发AvailabilityChangeEvent事件。
- 如果启动遇到异常,触发ApplicationFailedEvent事件。
此外,还会在ApplicationPreparedEvent事件与ApplicationStartedEvent事件之间触发如下2个容器事件:
- web服务器初始化完成后触发WebServerInitializedEvent事件;
- 如果使用servlet web服务器,则触发ServletWebServerInitializedEvent
- 如果使用反应式web服务器,则触发ReactiveWebServerInitializedEvent
- 在刷新ApplicationContext时触发ContextRefreshEvent事件。
由于事件监听器采用同一个线程来执行,因此不应该在监听器中执行耗时操作,如果确实需要,建议使用ApplicationRunner或CommandLineRunner接口。- // 定义事件监听器,负责监听ApplicationStartedEvent事件
- // 实现ApplicationContextAware接口,可用于获取所在的Spring容器
- public class MyListener implements ApplicationContextAware,
- ApplicationListener
- {
- private ApplicationContext ctx;
-
- // 监听ApplicationStartedEvent事件
- @Override
- public void onApplicationEvent(ApplicationStartedEvent event)
- {
- // 获取激发事件的容器
- ConfigurableApplicationContext c = event.getApplicationContext();
- if (c == ctx)
- {
- System.out.println("-----激发事件的容器与监听器所在容器相同-----");
- }
- // 后面的代码可插入任意自定义的处理
- System.out.println("========执行自定义处理=======");
- }
- // 接口注入方法,通过该方法可访问Spring容器
- @Override
- public void setApplicationContext(ApplicationContext ctx) throws BeansException
- {
- this.ctx = ctx;
- }
- }
复制代码 上面这些时间都是通过Spring框架事件机制来发布的,这种机制保证子容器触发的事件也会自动触发父容器的监听器。因此,如果采用层次结构的容器,监听器有可能接收到同一类型事件的多个实例--它们来自不同的容器。
为了让监听器能区分事件来自哪个容器,可以用监听器依赖注入的 容器与事件的容器进行比较,将容器依赖注入事件监听器通过如下2种方式:
- 接口注入:让监听器实现ApplicationContextAware接口,Spring容器会被注入监听器。
- 普通注入:如果监听器是容器中的bean,可使用@Autowired完成依赖注入。
除了监听器,SpringBoot提供了ApplicationContextInitializer对 Spring容器执行初始化。ApplicationContextInitializer接口的实现类被称为“初始化器”,实现类必然实现initialize(C ctx)方法。有了实现类,通过如下方式来注册初始化器:
- 调用SpringApplication的addInitializer()或SpringApplicationBuilder的initializers()添加初始化器。
- 使用/META-INF/spring.factories来配置初始化器:
- org.springframework.context.ApplicationContextInitializer=org.crazyit.app.MyInitializer
复制代码
- public class MyInitializer implements
- ApplicationContextInitializer<ConfigurableApplicationContext>
- {
- // 大部分时候springboot会自动执行一些初始化工作,不需要开发者执行额外初始化,如果要对Spring容器进行某些与业务相关的特定设置,或者让SpringBoot整合某些前沿的、官方未支持的框架,则可在此处执行自定义初始化
- @Override
- public void initialize(ConfigurableApplicationContext configurableApplicationContext)
- {
- // 接下来的代码可对Spring容器执行任意的初始化
- System.out.println("====模拟对Spring容器执行初始化====");
- }
- }
复制代码 配置环境后处理器
如果想在Environment对象创建后、Spring容器刷新前对Environment对象进行定制,SpringBoot提供配置环境后处理器--实现EnvironmentPostProcessor接口的类被称为“配置环境后处理器”。实现类必然实现postProcessEnvironment(environment,application)方法,通过该方法的参数对Environment进行定制,比如将自定义配置文件的属性加载到配置环境中。
可通过/META-INF/spring.factories来注册配置环境后处理器:- org.springframework.boot.env.EnvironmentPostProcessor=org.crazyit.app.FkEnvironmentPostProcessor
复制代码- public class FkEnvironmentPostProcessor implements EnvironmentPostProcessor
- {
- private final PropertiesPropertySourceLoader loader = new PropertiesPropertySourceLoader();
- @Override
- public void postProcessEnvironment(ConfigurableEnvironment environment,
- SpringApplication application)
- {
- // 指定自定义的配置文件,加载类加载路径下的fk/fk.properties文件
- Resource path = new ClassPathResource("fk/fk.properties");
- // 加载自定义配置文件
- PropertySource<?> ps = loadProperty(path);
- System.out.println("fkjava.name: " + ps.getProperty("fkjava.name"));
- System.out.println("fkjava.age: " +ps.getProperty("fkjava.age"));
- // 将PropertySource中的属性添加到Environment配置环境中
- environment.getPropertySources().addLast(ps);
- }
- private PropertySource<?> loadProperty(Resource path)
- {
- if (!path.exists())
- {
- throw new IllegalArgumentException("资源: " + path + " 不存在");
- }
- try
- {
- // 加载path对应的配置文件
- return this.loader.load("custom-resource", path).get(0);
- }
- catch (IOException ex)
- {
- throw new IllegalStateException("加载配置文件出现错误: " + path, ex);
- }
- }
- }
复制代码
ApplicationRunner和CommandLineRunner
ApplicationRunner和CommandLineRunner也属于SpringApplication的初始化API,在SpringApplication的run方法完成之前,ApplicationRunner和CommandLineRunner的实现类会自动调用接口中的run()方法。
由此可见ApplicationRunner和CommandLineRunner适合在程序启动后,对外提供服务之前进行一些特定的初始化处理。
ApplicationRunner和CommandLineRunner功能基本一样,都只定义了一个run方法,区别是参数类型不同:
- ApplicationRunner中run方法的参数类型是ApplicationArguments,用于访问运行SpringApplication的参数。ApplicationArguments使用如下方法来获取运行参数:
- SetgetOptionNames():获取参数中所有的选项参数名,以双横线--开头的参数才是选项参数
- ListgetOptionValues(String name):根据选项名获取选项值
- ListgetNonOptionArgs():获取非选项参数
- String[] getSourceArgs():获取原始的参数
- CommandLineRunner中的run方法的参数类型是String...,可直接代表SpringApplication的参数
- @Component
- public class FkRunner implements ApplicationRunner {
- // 改方法在Spring Boot应用启动完成后执行,
- @Override
- public void run(ApplicationArguments args) throws Exception {
- System.out.println("====模拟对Spring容器执行初始化====" +
- "\n" + "应用启动完成后执行的代码" +
- "\n" + "getOptionNames = " + args.getOptionNames() +
- "\n" + "getSourceArgs = " + args.getSourceArgs() +
- "\n" + "getNonOptionArgs = " + args.getNonOptionArgs());
- }
- }
复制代码
结果:
“--”开头的参数会覆盖application.properties等配置文件中的属性。
创建非web应用
SpringApplication会按如下规则来创建ApplicationContext容器:
- 如果项目添加了SpringMVC依赖,则使用AnnotationConfigServletWebServerApplicationContext作为Spring容器。
- 如果添加了Spring WebFlux依赖,则使用AnnotationConfigReactiveWebServerApplicationContext作为Spring容器。
- 否则使用AnnotationConfigApplicationContext作为Spring容器。
如果项目同时添加了SpringMVC依赖和Spring WebFlux依赖,依然使用AnnotationConfigServletWebServerApplicationContext作为Spring容器。
可以调用SpringApplication的setWebApplicationType()方法来设置Spring容器类型,该方法 接收WebApplicationType类型的枚举值:
- NONE:运行非Web应用,不启动内嵌的Web服务器
- REACTIVE:运行反应式Web应用,启动内嵌的反应式Web服务器
- SERVLET:运行Servlet的Web应用,启动内嵌的Servlet Web服务器
如果要创建非Web应用:- public class App {
- public static void main(String[] args) {
- var application = new SpringApplication(App.class);
- application.setWebApplicationType(WebApplicationType.NONE);
- // 创建Spring容器、运行Spring Boot应用
- application.run(args);
- }
- }
复制代码 通过ApplicationArguments访问应用参数
SpringBoot会自动将ApplicationArguments注入容器中,因此使用 @Autowired依赖注入实例变量即可获取对象。- @RestController
- public class HelloController
- {
- // 依赖注入容器中的ApplicationArguments Bean
- @Autowired
- private ApplicationArguments args;
- @GetMapping("/")
- public void test()
- {
- System.out.println("访问应用的运行参数");
- System.out.println("getSourceArgs:" + Arrays.toString(args.getSourceArgs()));
- System.out.println("getOptionValues:" + args.getOptionValues("book.name"));
- System.out.println("getNonOptionArgs:" + args.getNonOptionArgs());
- }
- }
复制代码 运行结果:
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |