• SpringBoot之SpringApplication


    简介

    可以用于从java主方法中引导和启动Spring应用程序的类,在默认情况下,通过以下步骤来启动应用:

    • 创建一个ApplicationContext实例
    • 注册CommandLinePropertySource用来暴露命令行参数作为spring的属性
    • refresh applicationContext,加载所有的singleton bean
    • 触发任何CommandLineRunner bean

    自定义SpringApplication

    默认的启动方式,代码如下,不做深入介绍

    1
    SpringApplication.run(AppBoot1.class, args);

    当然也可以做一些定制,如在Banner里面介绍的自定义banner,还有添加Listener等诸多设置,格式如下:

    1
    2
    3
    SpringApplication app = new SpringApplication(MySpringConfiguration.class);
    app.setBannerMode(Banner.Mode.OFF);
    app.run(args);

    使用Fluent Builder API进行构建

    先上代码,如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    new SpringApplicationBuilder()
    .sources(ParentConfig.class)
    .child(Child1Config.class)
    .sibling(Child2Config.class)
    .banner(new AppBanner())
    .logStartupInfo(false)
    .run(args);



    //@Configuration

    @PropertySource("classpath:/parent.properties")
    public class {
    }

    // Child1Config.class


    @PropertySource("classpath:/child1.properties")
    public class Child1Config {

    }

    // Child2Config.class


    @PropertySource("classpath:/child2.properties")
    public class Child2Config {

    }

    使用SpringApplicationBuilder在构建分层的ApplicationContext应用的时候非常便利,但是官网给的代码示例很简单,对于一些初次介入的人可能理解上不是那么透彻。就上面的代码做一些简单的介绍

    • .sources(ParentConfig.class)该方法是用来配置父配置,或主配置的。但是有坑!!!!请看上面的ParentConfig的代码,注解我使用的是@SpringBootApplication,如果你使用的是springboot的1.x的版本,那么你会很顺利,如果你正在研究springboot 2.x的版本,你会发现无论如何也无法启动成功(我被坑的好惨)…。聪明的人也许看到我的注释,没错换成@Configuration之后就可以正常工作。但是很抱歉,这是为什么暂时还没找到原因,在github上请教暂时也没得到正确的结果,后面继续研究,如果有人发现了其中的原因,请通知我一下
    • .child(Child1Config.class)那么通过名字就可以看到是子环境了,Child1Config就是child1的配置文件。也许你的应用里有多个child,那么你可能会想用多个child().child(),那么这样你的第二个child不是parent的child,而是第一个child的child,parent的孙子。想要实现多个同级的孩子,可以使用代码中的.sibling(Child2Config.class)方法。这里同样存在一个springboot的版本改动问题,那就是如果你要在配置文件里面为child1配置一个context path,那么在版本1里面的方法是server.contextPath=child1,但是如果使用版本2的朋友就需要做一点小改动了,改为server.servlet.contextPath=child1

    可以参照github上的代码进行详细的理解github

    事件和监听器

    spring里面的监听器有三种实现方式:

    • @EventListener注解方式
    • 实现ApplicationListener接口
    • 实现SmartApplicationListener接口

    上面三种方式代码分别为

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Component
    public class AnnotationRegisterListener {

    @EventListener
    public void register(UserRegisterEvent event) {
    User user = event.getUser();

    System.out.println("AnnotationRegisterListener " + user.getName() + ", " + user.getPassword());
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @Component
    public class RegisterListener implements ApplicationListener<UserRegisterEvent>{

    @Override
    public void onApplicationEvent(UserRegisterEvent event) {

    User user = event.getUser();

    System.out.println("RegisterListener " + user.getName() + ", " + user.getPassword());
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    @Component
    public class UserRegisterListener implements SmartApplicationListener {

    /**
    * 该方法返回true&supportsSourceType同样返回true时,才会调用该监听内的onApplicationEvent方法
    * @param eventType
    * @return
    */
    @Override
    public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
    return UserRegisterEvent.class.isAssignableFrom(eventType);
    }

    /**
    * 该方法返回true&supportsEventType同样返回true时,才会调用该监听内的onApplicationEvent方法
    * @param sourceType
    * @return
    */
    @Override
    public boolean supportsSourceType(@Nullable Class<?> sourceType) {
    return UserService.class.isAssignableFrom(sourceType);
    }

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
    UserRegisterEvent userRegisterEvent = (UserRegisterEvent) event;
    User user = userRegisterEvent.getUser();
    System.out.println("UserRegisterListener " + user.getName() + ", " + user.getPassword());
    }

    /**
    * 同步情况下监听执行的顺序
    * @return
    */
    @Override
    public int getOrder() {
    return 3;
    }
    }

    前两种实现方式比较简单,稍微介绍一下第三种,这种实现方式必须在supportsEventType和supportsSourceType同时返回true的情况下才会执行事件,具体如何返回true和false就看你自己的业务实现。在这里我使用了jdk的isAssignableFrom方法来判断父子关系的。
    上面两种方式事件的执行顺序是无序的,第三种提供了一种指定属性的方法getOrder()

    具体的isAssignableFrom是如何使用的,请看如下代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    public class Animal {
    }

    public class Bird 大专栏  SpringBoot之SpringApplicationn class="keyword">extends Animal implements Fly {
    }

    public class Dove extends Bird {
    }

    public class Penguin extends Bird {
    }

    public interface Fly {
    }

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

    assignOut(Animal.class, Animal.class);
    assignOut(Animal.class, Bird.class);
    assignOut(Bird.class, Animal.class);
    assignOut(Animal.class, Dove.class);
    assignOut(Penguin.class, Dove.class);
    assignOut(Fly.class, Bird.class);
    assignOut(Fly.class, Dove.class);


    }

    static void assignOut(Class cls1, Class cls2) {
    System.out.println(cls1.getSimpleName() + " isAssignableFrom " + cls2.getSimpleName() + " : " + cls1.isAssignableFrom(cls2));
    }
    }

    // 输出结果
    Animal isAssignableFrom Animal : true
    Animal isAssignableFrom Bird : true
    Bird isAssignableFrom Animal : false
    Animal isAssignableFrom Dove : true
    Penguin isAssignableFrom Dove : false
    Fly isAssignableFrom Bird : true
    Fly isAssignableFrom Dove : true

    SpringBoot启动过程中的事件及监听器

    • ApplicationStartingEvent 应用开始启动时执行,在初始化listeners and initializers之前执行
    • ApplicationEnvironmentPreparedEvent 环境准备好开始执行,上下文创建之前执行
    • ApplicationPreparedEvent 应用准备好开始执行,即所有bean都已经加载完毕之后,在refresh方法执行之前执行
    • ApplicationStartedEvent 应用启动后开始执行,即上下文被refreshed之后,任何的application和command-line runners调用前执行
    • ApplicationReadyEvent 应用启动完毕开始执行,任何的application和command-line runners调用后执行,从此后可以服务request了
    • ApplicationFailedEvent 启动时遇到异常执行

    Listener的写法很简单,只以ApplicationStartingEvent为例,其他类似

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public class MyApplicationStartedListener implements ApplicationListener<ApplicationStartedEvent> {
    @Override
    public void onApplicationEvent(ApplicationStartedEvent applicationStartedEvent) {
    System.out.println("MyApplicationStartedListener...");
    }
    }

    在main方法注入
    SpringApplicationBuilder builder = new SpringApplicationBuilder();
    builder.listeners(new MyApplicationStartingListener());
    builder.listeners(new MyApplicationEnvironmentPreparedListener());
    builder.listeners(new MyApplicationPreparedListener());
    builder.listeners(new MyApplicationStartedListener());
    // builder.listeners(new MyApplicationReadyListener());
    builder.sources(AppBoot.class);
    builder.run(args);
    或者可以在META-INF/spring.factories中配置
    org.springframework.context.ApplicationListener=com.smxknife.springboot.v2.ex03.listener.MyApplicationReadyListener

    看一下输出结果

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    2018-03-03 01:24:06.387  INFO 5399 --- [  restartedMain] .ConditionEvaluationDeltaLoggingListener : Condition evaluation unchanged
    2018-03-03 01:28:58.909 INFO 5399 --- [ Thread-28] ConfigServletWebServerApplicationContext : Closing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@58c49e73: startup date [Sat Mar 03 01:24:05 CST 2018]; root of context hierarchy
    2018-03-03 01:28:58.913 INFO 5399 --- [ Thread-28] o.s.j.e.a.AnnotationMBeanExporter : Unregistering JMX-exposed beans on shutdown
    MyApplicationStartingListener...
    MyApplicationEnvironmentPreparedListener...

    . ____ _ __ _ _
    / / ___'_ __ _ _(_)_ __ __ _
    ( ( )___ | '_ | '_| | '_ / _` |
    / ___)| |_)| | | | | || (_| | ) ) ) )
    ' |____| .__|_| |_|_| |___, | / / / /
    =========|_|==============|___/=/_/_/_/
    :: Spring Boot :: (v2.0.0.RC2)

    2018-03-03 01:28:59.534 INFO 5399 --- [ restartedMain] com.smxknife.springboot.v2.ex03.AppBoot : Starting AppBoot on ShaoYundeMacBook-Pro.local with PID 5399 (/Users/ShaoYun/local/workstation/programs/smxknife/spring-boot/spring-boot-v2/spring-boot-v2-web/target/classes started by ShaoYun in /Users/ShaoYun/local/workstation/programs/smxknife/spring-boot)
    2018-03-03 01:28:59.536 INFO 5399 --- [ restartedMain] com.smxknife.springboot.v2.ex03.AppBoot : No active profile set, falling back to default profiles: default
    MyApplicationPreparedListener...
    2018-03-03 01:28:59.539 INFO 5399 --- [ restartedMain] ConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@45b78eb1: startup date [Sat Mar 03 01:28:59 CST 2018]; root of context hierarchy
    2018-03-03 01:29:00.304 INFO 5399 --- [ restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 10101 (http)
    2018-03-03 01:29:00.305 INFO 5399 --- [ restartedMain] o.apache.catalina.core.StandardService : Starting service [Tomcat]
    2018-03-03 01:29:00.305 INFO 5399 --- [ restartedMain] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/8.5.28
    2018-03-03 01:29:00.316 INFO 5399 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
    2018-03-03 01:29:00.317 INFO 5399 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 778 ms
    2018-03-03 01:29:00.338 INFO 5399 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean : Servlet dispatcherServlet mapped to [/]
    2018-03-03 01:29:00.338 INFO 5399 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*]
    2018-03-03 01:29:00.338 INFO 5399 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
    2018-03-03 01:29:00.338 INFO 5399 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'httpPutFormContentFilter' to: [/*]
    2018-03-03 01:29:00.338 INFO 5399 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'requestContextFilter' to: [/*]
    2018-03-03 01:29:00.492 INFO 5399 --- [ restartedMain] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@45b78eb1: startup date [Sat Mar 03 01:28:59 CST 2018]; root of context hierarchy
    2018-03-03 01:29:00.529 INFO 5399 --- [ restartedMain] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/register]}" onto public java.lang.String com.smxknife.springboot.v2.ex03.web.controller.UserController.register(com.smxknife.springboot.v2.ex03.domain.User)
    2018-03-03 01:29:00.533 INFO 5399 --- [ restartedMain] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
    2018-03-03 01:29:00.534 INFO 5399 --- [ restartedMain] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
    2018-03-03 01:29:00.555 INFO 5399 --- [ restartedMain] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
    2018-03-03 01:29:00.555 INFO 5399 --- [ restartedMain] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
    2018-03-03 01:29:00.591 INFO 5399 --- [ restartedMain] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
    2018-03-03 01:29:00.648 INFO 5399 --- [ restartedMain] o.s.b.d.a.OptionalLiveReloadServer : LiveReload server is running on port 35729
    2018-03-03 01:29:00.720 INFO 5399 --- [ restartedMain] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
    2018-03-03 01:29:00.738 INFO 5399 --- [ restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 10101 (http) with context path ''
    2018-03-03 01:29:00.739 INFO 5399 --- [ restartedMain] com.smxknife.springboot.v2.ex03.AppBoot : Started AppBoot in 1.585 seconds (JVM running for 3711.163)
    MyApplicationStartedListener...
    MyApplicationReadyListener...
    2018-03-03 01:29:00.741 INFO 5399 --- [ restartedMain] .ConditionEvaluationDeltaLoggingListener : Condition evaluation unchanged

    Listener的相关代码可以参照 github

  • 相关阅读:
    Windows消息机制
    inherited 为什么可以调用父类的private函数? [问题点数:100分,结帖人:h2plus0]
    C++Buidler6中需要注意的几个问题
    BGA封装芯片拆装全程纪实
    Delphi组件开发教程指南(四)组件生成过程(TWinControl)
    Delphi技巧集六 (等待执行完一个外部程序再执行另一个程序)
    C++ Builder高级应用开发指南
    干掉“Spirale”病毒
    完全看懂新世代x86指令集結構
    Delphi 组件撰写常问问题delphi 在整合环境中如何找出组件所产生的问题
  • 原文地址:https://www.cnblogs.com/lijianming180/p/12251617.html
Copyright © 2020-2023  润新知