栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

5、springboot原理分析-监听机制

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

5、springboot原理分析-监听机制

文章目录
  • 一、java监听机制
  • 二、SpringBoot 监听机制
  • 三、环境搭建
    • 1、创建springboot项目
    • 2、实现上述四个接口
      • MyApplicationContextInitializer
      • MyApplicationRunner
      • MyCommandLineRunner
      • MySpringApplicationRunListener
  • 四、分析`MyApplicationRunner` 和`MyCommandLineRunner`
    • 相同点:
    • 不同点:
    • 作用:
    • 两个方法的参数有何区别
  • 五、分析`MyApplicationContextInitializer` 和`MySpringApplicationRunListener`

一、java监听机制

springboot的监听机制,其实就是对java提供监听机制的封装。
为此我们可以先了解一下java的监听机制:
Java中的事件监听机制定义了以下几个角色:

  1. 事件(Event):要求所有的event继承 java.util.EventObject 类的对象
  2. 事件源(Source):可以监听任意对象Object、属性的创建和销毁,等 一些生命周期的变化情况。
  3. 监听器(Listener):要求所有的监听器实现 java.util.EventListener 接口的对象
二、SpringBoot 监听机制

之所以说springboot就是对java监听机制的封装,是因为SpringBoot 在项目启动时,会对几个监听器进行回调,我们可以实现这些监听器接口,在项目启动时完成一些操作。

今天我们主要介绍这四个:

  • ApplicationContextInitializer
  • SpringApplicationRunListener
  • CommandLineRunner
  • ApplicationRunner
    接下来我们通过代码来演示效果。
三、环境搭建 1、创建springboot项目

pom文件为:



    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.1.8.RELEASE
         
    
    com.example
    springboot-listener
    0.0.1-SNAPSHOT
    springboot-listener
    Demo project for Spring Boot

    
        1.8
    

    
        
            org.springframework.boot
            spring-boot-starter
        

        
            org.springframework.boot
            spring-boot-starter-test
            test
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    



创建启动类:

package com.example.springbootlistener;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringbootListenerApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootListenerApplication.class, args);
    }

}

2、实现上述四个接口

代码结构为:

下面依次列出各个实现类:

MyApplicationContextInitializer
package com.example.springbootlistener.listener;

import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;

@Component
public class MyApplicationContextInitializer implements ApplicationContextInitializer {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        System.out.println("ApplicationContextInitializer....initialize");
    }
}

MyApplicationRunner
package com.example.springbootlistener.listener;

import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

import java.util.Arrays;


@Component
public class MyApplicationRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("ApplicationRunner...run");
    }
}

MyCommandLineRunner
package com.example.springbootlistener.listener;

import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Component
public class MyCommandLineRunner implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("CommandLineRunner...run");
    }
}


MySpringApplicationRunListener
package com.example.springbootlistener.listener;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.stereotype.Component;


@Component
public class MySpringApplicationRunListener implements SpringApplicationRunListener {


    @Override
    public void starting() {
        System.out.println("starting...项目启动中");
    }

    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {
        System.out.println("environmentPrepared...环境对象开始准备");
    }

    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
        System.out.println("contextPrepared...上下文对象开始准备");
    }

    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {
        System.out.println("contextLoaded...上下文对象开始加载");
    }

    @Override
    public void started(ConfigurableApplicationContext context) {
        System.out.println("started...上下文对象加载完成");
    }

    @Override
    public void running(ConfigurableApplicationContext context) {
        System.out.println("running...项目启动完成,开始运行");
    }

    @Override
    public void failed(ConfigurableApplicationContext context, Throwable exception) {
        System.out.println("failed...项目启动失败");
    }
}

另外,我们在每个实现类上都加入了@Component 注解,主要目的就是为了将上面四个实现类注入到ioc容器中。
下面我们启动一下项目,看一下日志:
通过日志我们可以发现,仅仅有两个监听器MyApplicationRunner 和MyCommandLineRunner生效了。另外两个没有生效执行,是因为MyApplicationContextInitializer 和MySpringApplicationRunListener 需要配置才能生效。

四、分析MyApplicationRunner 和MyCommandLineRunner

MyApplicationRunner

MyCommandLineRunner
通过图片上的代码我们可以看出来,这两个接口中的方法基本是一样的,包括方法名,返回值,执行时间等,执行时间都是在项目启动后,唯一不一样的就是参数类型。

相同点:

都是在项目启动后执行。

不同点:

方法参数不一样。

作用:

因为都是在项目启动完成后执行,并且是被自动调用。那么我们可以在项目启动后执行一些方法,比如加载数据缓存(将数据库数据加载到redis),设置初始值,初始化数据库等。

两个方法的参数有何区别

我们先打印一下两个方法的参数:

MyApplicationRunner

MyCommandLineRunner
再次启动项目,查看日志:
均显示为空。

我们可以在idea 中设置值:
启动项目,查看日志:


为此,我们发现这两个接口并没直接的区别,我们在项目中需要的话,任选其一就好了。

五、分析MyApplicationContextInitializer 和MySpringApplicationRunListener

首先让这个监听器生效的话,需要我们配置一下:spring.factories
我们需要在resources下创建meta-INF,然后再meta-INF 下创建spring.factories。

那么应该如何配置呢,比如key和value应该是什么呢。
举个例子,MyApplicationContextInitializer是实现的ApplicationContextInitializer接口,那么我们就复制ApplicationContextInitializer接口的全路径名作为key值,MyApplicationContextInitializer的全路径名为value值。
为此我们先配置MyApplicationContextInitializer,配置如下:

org.springframework.context.ApplicationContextInitializer=com.example.springbootlistener.listener.MyApplicationContextInitializer

启动项目,查看日志:
我们可以看到这个监听器是在图表banner打印之后,容器加载之前执行的,为此我们可以在这个监听器中做一些检查工作,比如检查资源是否存在等。

接下来我按照上面的方式配置MySpringApplicationRunListener。

org.springframework.context.ApplicationContextInitializer=com.example.springbootlistener.listener.MyApplicationContextInitializer

org.springframework.boot.SpringApplicationRunListener=com.example.springbootlistener.listener.MySpringApplicationRunListener

启动项目,查看日志:
我们可以看到报错的原因是,没有init的匹配方法。这是一个构造方法,需要两个参数,参数一为:SpringApplication,参数二为:字符串。

我可以进入接口SpringApplicationRunListener 中,看一下他的实现类:
我们发现有两个实现类,一个是我们自己实现的,一个是spring实现的,我们进入spring的实现类EventPublishingRunListener。
发现实现类EventPublishingRunListener中有个一个构造方式,有两个参数,分别是SpringApplication 和字符串数组,,其实参数SpringApplication就是我们项目的启动事件源。为此我们也可以在自己的监听器MySpringApplicationRunListener中增加一个构造函数:

package com.example.springbootlistener.listener;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.stereotype.Component;
@Component
public class MySpringApplicationRunListener implements SpringApplicationRunListener {

    public MySpringApplicationRunListener(SpringApplication application, String[] args) {
    }

   
  .....省略
}

此时代码是报错的,是因为我们上面增加 @component注解的,所以要注入application和args。为此我们将@component注解删除就可以了。然后启动项目查看日志:
根据日志,大家可以探索一下springboot的启动生命周期的东西,在对应的时机实现自己的功能业务。。
springboot定义的事件我们可以在springboot包下找到:

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/325371.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号