Spring boot监听器、启动器、异步调用、bean定义

Spring boot异步调用

package com.yasuo.application.listener;

import com.yasuo.application.event.YasuoApplicationEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Component;

/**
 * @desc: 用@EventListener装饰具体方法  然后将监听器加入spring容器中
 * @auther: MateEgg
 * @date: 2018/9/24 01:39
 * @version: 1.0
 */
@Slf4j
@EnableAsync
@Component
public class YasuoApplicationListenerThree {

    /**
     * @param yasuoApplicationEvent
     * @annotation Async表示异步方法 此方法被调用的时候异步执行
     */
    @Async
    @EventListener
    public void onApplicationEvent(YasuoApplicationEvent yasuoApplicationEvent) throws InterruptedException {
        for (int i = 0; i < 100; i++) {
            log.info("YasuoApplicationListenerThree is executing...{}", i);
            Thread.currentThread().sleep(1000);
        }

    }
}

Spring boot的事件监听机制

除了通常的Spring Framework事件之外,例如 ContextRefreshedEventSpringApplication发送一些额外的应用程序事件。

某些事件实际上是在ApplicationContext创建之前触发的,因此您无法在这些事件上注册侦听器@Bean。您可以使用SpringApplication.addListeners(…)方法或 SpringApplicationBuilder.listeners(…)方法注册它们 。

如果您希望自动注册这些侦听器,你可以在项目中创建META-INF/spring.factories并使用该org.springframework.context.ApplicationListener键引用侦听器 ,如以下示例所示:

org.springframework.context.ApplicationListener = com.example.project.MyListener

应用程序运行时,应按以下顺序发送应用程序事件:

  1. ApplicationStartingEvent在运行开始,但除了监听器注册和初始化以外的任何处理之前,
  2. ApplicationEnvironmentPreparedEvent在Environment将被用于已知的上下文,但在上下文被创建前,
  3. ApplicationPreparedEvent在refresh开始前,但在bean定义已被加载后,
  4. ApplicationStartedEvent上下文已被刷新后,但是任何应用程序和命令行被调用前。
  5. ApplicationReadyEvent应用程序和命令行被调用后发送。它表示应用程序已准备好为请求提供服务。
  6. ApplicationFailedEvent如果在启动时异常发送。
自定义监听器注册的几种方式:
定义事件:
package com.yasuo.application.event;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEvent;
import org.springframework.stereotype.Component;

/**
 * @desc: 继承ApplicationEvent自定义监听事件
 * @auther: MateEgg
 * @date: 2018/9/24 00:58
 * @version: 1.0
 */
public class YasuoApplicationEvent extends ApplicationEvent {

    public YasuoApplicationEvent(Object source) {
        // 父类的构造方法是对父类中的一个Object类型的字段source初始化
        super(source);
    }

}
第一种监听器注册方式:
package com.yasuo.application.listener;

import com.yasuo.application.event.YasuoApplicationEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

/**
 * @desc: 第一种监听器注册方式 利用@Component注解将其加入spring容器中
 * @auther: MateEgg
 * @date: 2018/9/24 00:57
 * @version: 1.0
 */
@Slf4j
@Component
public class YasuoApplicationListenerOne implements ApplicationListener<YasuoApplicationEvent> {

    @Override
    public void onApplicationEvent(YasuoApplicationEvent yasuoApplicationEvent) {
        log.info("YasuoApplicationListenerOne is executing...");
    }
}
第二种监听器注册方式:
package com.yasuo.application.listener;

import com.yasuo.application.event.YasuoApplicationEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;

/**
 * @desc: 第二种监听器注册方式  在yaml文件中配置监听器的路径
 * @auther: MateEgg
 * @date: 2018/9/24 01:04
 * @version: 1.0
 */
@Slf4j
public class YasuoApplicationListenerTwo implements ApplicationListener<YasuoApplicationEvent> {

    @Override
    public void onApplicationEvent(YasuoApplicationEvent yasuoApplicationEvent) {
        log.info("YasuoApplicationListenerTwo is executing...");
    }
}
context:
  listener:
    classes: com.yasuo.application.listener.YasuoApplicationListenerTwo
第三种监听器注册方式:
package com.yasuo.application.listener;

import com.yasuo.application.event.YasuoApplicationEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Component;

/**
 * @desc: 用@EventListener装饰具体方法  然后将监听器加入spring容器中
 * @auther: MateEgg
 * @date: 2018/9/24 01:39
 * @version: 1.0
 */
@Slf4j
@EnableAsync
@Component
public class YasuoApplicationListenerThree {

    /**
     * @param yasuoApplicationEvent
     * @annotation Async表示异步方法 此方法被调用的时候异步执行
     */
    @Async
    @EventListener
    public void onApplicationEvent(YasuoApplicationEvent yasuoApplicationEvent) throws InterruptedException {
        log.info("YasuoApplicationListenerThree is executing...");
    }
}
ApplicationContext发布事件:
package com.yasuo.application;

import com.yasuo.application.event.YasuoApplicationEvent;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(Application.class, args);
        run.publishEvent(new YasuoApplicationEvent("Test ApplicationListeners"));
    }
}

输出结果:
2018-09-24 04:16:31.872  INFO 17396 --- [           main] c.y.a.l.YasuoApplicationListenerOne      : YasuoApplicationListenerOne is executing...
2018-09-24 04:16:31.868  INFO 17396 --- [           main] c.y.a.l.YasuoApplicationListenerTwo      : YasuoApplicationListenerTwo is executing...
2018-09-24 04:16:31.876  INFO 17396 --- [cTaskExecutor-1] c.y.a.l.YasuoApplicationListenerThree    : YasuoApplicationListenerThree is executing...

监听spring boot事件:

配置监听器:
package com.yasuo.application.listener;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.event.*;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;

/**
 * @desc: 监听spring boot内置六种事件
 * @auther: MateEgg
 * @date: 2018/9/24 02:17
 * @version: 1.0
 */
@Slf4j
public class YasuoApplicationListenerFour implements ApplicationListener<ApplicationEvent> {

    @Override
    public void onApplicationEvent(ApplicationEvent applicationEvent) {
        if (applicationEvent instanceof ApplicationStartingEvent) {
            //此时log.info()不会输出日志所以替换为system.out.println()
//            log.info("YasuoApplicationListenerFour is executing...{}", "ApplicationStartingEvent");
            System.out.println("Spring boot 开始启动");
            return;
        }

        if (applicationEvent instanceof ApplicationEnvironmentPreparedEvent) {
            log.info("YasuoApplicationListenerFour is executing...{}", "ApplicationEnvironmentPreparedEvent");
            return;
        }

        if (applicationEvent instanceof ApplicationPreparedEvent) {
            log.info("YasuoApplicationListenerFour is executing...{}", "ApplicationPreparedEvent");
            return;
        }

        if (applicationEvent instanceof ApplicationStartedEvent) {
            log.info("YasuoApplicationListenerFour is executing...{}", "ApplicationStartedEvent");
            return;
        }

        if (applicationEvent instanceof ApplicationReadyEvent) {
            log.info("YasuoApplicationListenerFour is executing...{}", "ApplicationReadyEvent");
            return;
        }

        if (applicationEvent instanceof ApplicationFailedEvent) {
            log.info("YasuoApplicationListenerFour is executing...{}", "ApplicationFailedEvent");
            return;
        }

        log.info("YasuoApplicationListenerFour is executing...{}", "无人认领");
    }

}
META-INF/spring.factories文件:
# Initializers
org.springframework.context.ApplicationContextInitializer=

# Application Listeners
org.springframework.context.ApplicationListener=\
com.yasuo.application.listener.YasuoApplicationListenerFour

# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=

# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=

# Failure analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=

# Template availability providers
org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=
输出结果:
// 只截取了部分输出结果
Spring boot 开始启动
2018-09-24 04:21:37.735  INFO 16004 --- [           main] c.y.a.l.YasuoApplicationListenerFour     : YasuoApplicationListenerFour is executing...ApplicationEnvironmentPreparedEvent

Spring boot命令行与系统属性:

获取系统属性:
System.out.println("系统属性env=" + System.getProperty("env"));
命令行属性:

命令行属性会作为参数传给main方法

package com.yasuo.application.args;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @desc:
 * @auther: MateEgg
 * @date: 2018/9/24 04:42
 * @version: 1.0
 */
@Component
public class YasuoApplicationArguments {

    @Autowired
    public YasuoApplicationArguments(ApplicationArguments args) {
        // 判断命令行参数是否包含name sex
        boolean name = args.containsOption("name");
        boolean sex = args.containsOption("sex");
        // 根据key获取命令行参数的值
        System.out.println(args.getOptionValues("name"));
        System.out.println(args.getOptionValues("sex"));
        // 获取不规范的命令行参数  不是 --key=value 格式
        List<String> others = args.getNonOptionArgs();
        System.out.println(others);
    }
}
获取上下文环境:

命令行属性和系统属性以及配置文件的值都是可以通过上下文环境获取

package com.yasuo.application.args;

import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

/**
 * @desc:
 * @auther: MateEgg
 * @date: 2018/9/24 05:16
 * @version: 1.0
 */
@Component
public class YasuoEnvironmentAware implements EnvironmentAware {

    @Override
    public void setEnvironment(Environment environment) {

        String env1 = System.getProperty("env");
        System.out.println("系统属性env=" + env1);
        String env = environment.getProperty("env");
        System.out.println("YasuoEnvironmentAware结果..." + environment.getProperty("env") + "..." + environment.getProperty("name"));
    }
}
动态设置系统属性:

在spring boot的启动监听事件中设置、以便属性可以在整个spring生命中被使用

System.setProperty("laji", "MateEgg");

Spring boot内部接口分析:

bean初始化顺序:

bean初始化顺序:Constructor > @PostConstruct > InitializingBean > init-method

InitializingBean   // 初始化bean  执行顺序为Constructor > @PostConstruct >InitializingBean > init-method
Aware接口:

Aware接口作用:实现Aware接口可以让bean感知到自身的在spring 框架中的特定属性

BeanNameAware // 获取bean的名称 在bean的普通属性设置之后在 InitializingBean > init-method方法之前调用
BeanFactoryAware  // 获取beanFactory实例   在bean的普通属性设置之后在 InitializingBean > init-method方法之前调用
ApplicationContextAware   // 获取ApplicationContext实例   在bean的普通属性设置之后在 InitializingBean > init-method方法之前调用
启动器的作用:
ApplicationRunner    // 在容器启动的时候执行一些内容。比如读取配置文件,数据库连接之类的    在容器启动完成时执行       --参数为ApplicationArguments
CommandLineRunner    // 在容器启动的时候执行一些内容。比如读取配置文件,数据库连接之类的    在容器启动完成时执行     --参数为String 数组    参数来源为programe command  eg:--foo=foo

ApplicationArguments 在使用时可以直接注入 @Autowired private ApplicationArguments args;

BeanFactory和FactoryBean的区别

区别:BeanFactory是个Factory,也就是IOC容器或对象工厂,FactoryBean是个Bean。在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的。但对FactoryBean而言,这个Bean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似 。

FactoryBean表示它是一个Bean,不同于普通Bean的是:它是实现了FactoryBean<T>接口的Bean,根据该Bean的ID从BeanFactory中获取的实际上是FactoryBean的getObject()返回的对象,而不是FactoryBean本身,如果要获取FactoryBean对象,请在id前面加一个&符号来获取。

public interface FactoryBean<T> {    
   T getObject() throws Exception;    
   Class<?> getObjectType();    
   boolean isSingleton();    
} 
// 在该接口中还定义了以下3个方法。

// T getObject():返回由FactoryBean创建的bean实例,如果isSingleton()返回true,则该实例会放到Spring容器中单实例缓存池中。

// boolean isSingleton():返回由FactoryBean创建的bean实例的作用域是singleton还是prototype。

// Class<T> getObjectType():返回FactoryBean创建的bean类型。

Spring boot中bean的动态注册:

BeanDefinitionRegistryPostProcessor:
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)    // 继承BeanDefinitionRegistryPostProcessor实现postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)可以做到动态注册bean到spring中
BeanDefinitionRegistry:
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
void removeBeanDefinition(String beanName)
BeanDefinition getBeanDefinition(String beanName)
boolean containsBeanDefinition(String beanName)
//  以Map<String, BeanDefinition>的形式注册bean  提供了根据beanName删除和获取  beanDefiniation,得到持有的beanDefiniation的数目,根据beanName判断是否包含              beanDefiniation等的方法

SimpleBeanDefinitionRegistry是BeanDefinitionRegistry三种默认实现方式中的一种,用线程安全的ConcurrentHashMap的方式存储BeanDefinition private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(64);;

BeanDefinitionBuilder:
    private void registerTransactionManagerDefinitionBuilder(String transactionManagerName, BeanDefinitionRegistry beanFactory, String dataSourceName) {
        BeanDefinitionBuilder transactionManagerDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(DataSourceTransactionManager.class);
        transactionManagerDefinitionBuilder.addPropertyReference("dataSource", dataSourceName);
        beanFactory.registerBeanDefinition(transactionManagerName, transactionManagerDefinitionBuilder.getRawBeanDefinition());
    }

// 这是BeanDefinitionBuilder的简单的使用例子,通过构造方法与要实例化的类关联,通过addPropertyReference()方法为实例化的类添加属性,通过transactionManagerDefinitionBuilder.getRawBeanDefinition()方法转化为BeanDefintion类的实例
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容