带你了解Spring监听器

关于监听器

nacos也是利用监听器来进行集成的,所以还需要学习原理
要创建监听器需要俩个东西

  1. 事件
  2. 监听器

来个简单例子来看看

自定义事件

/***
 * 事件
 */
public class OrderEvent  extends ApplicationEvent {

    private String name;

    public OrderEvent(Object source, String name) {
        super(source);
        this.name = name;
    }

    public String getName() {
        return name;
    }
}
/***
 *
 * 监听器
 */
@Component
@Lazy
public class OrderEventListener implements ApplicationListener<OrderEvent> {

    // 基于注解的
    //@EventListener(OrderEvent.class)
    public void onApplicationEvent(OrderEvent event) {
        if(event.getName().equals("减库存")){
            System.out.println("减库存.......");
        }
    }
}

订单实体类

public class Order {
    private Integer id;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }
}

怎么用?

@Configuration
@ComponentScan(basePackages = {"com.tuling.event"})
public class MainConfig {

}
public class MainClass {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext ctx = 
		     new AnnotationConfigApplicationContext(MainConfig.class);

		//下单
		Order order =new Order();
		order.setId(1);
		System.out.println("下单");
		ctx.publishEvent(new OrderEvent(order,"减库存"));
		System.out.println("日志...");
	}
}

运行结果:
在这里插入图片描述
一旦发布事件,监听器就会起作用。库存和下单代码解耦,一般可以用MQ来解决,这里主要是看看效果。

原理就是观察者模式,一个订阅,一个消费

上边的是自定义事件,Spring中还有其他的事件

Event说明
ContextRefreshedEvent当容器被实例化或refreshed时发布.如调用refresh()方法, 此处的实例化是指所有的bean都已被加载,后置处理器都被激活,所有单例bean都已被实例化, 所有的容器对象都已准备好可使用. 如果容器支持热重载,则refresh可以被触发多次(XmlWebApplicatonContext支持热刷新,而GenericApplicationContext则不支持)
ContextStartedEvent当容器启动时发布,即调用start()方法, 已启用意味着所有的Lifecycle bean都已显式接收到了start信号
ContextStoppedEvent当容器停止时发布,即调用stop()方法, 即所有的Lifecycle bean都已显式接收到了stop信号 , 关闭的容器可以通过start()方法重启
ContextClosedEvent当容器关闭时发布,即调用close方法, 关闭意味着所有的单例bean都已被销毁.关闭的容器不能被重启或refresh

重点了解ContextRefreshedEventContextClosedEvent
ContextRefreshedEvent会在refresh方法,所有单例bean创建完成,会去发布的事件。

问题
怎么样可以在所有Bean创建完后做扩展代码?
通过源码可以看到
在这里插入图片描述
在这里插入图片描述
当所有bean实例化后,发动容器启动完毕事件。

ContextClosedEvent事件
在这里插入图片描述
这里举个例子,监听容器加载完毕的事件

@Component
public class ContextRefreshedEventListener {

    @EventListener(ContextRefreshedEvent.class)
    public void onApplicationEvent(ContextRefreshedEvent event)  {
         //root application context 没有parent,他就是老大.
        if(event.getApplicationContext().getParent() == null)
        {
            System.out.println("______________\n容器加载完毕\n———————");
        }

    }

}
public class MainClass {

	public static void main(String[] args) {
		AnnotationConfigApplicationContext ctx = 
		         new AnnotationConfigApplicationContext(MainConfig.class);
		//下单
		Order order =new Order();
		order.setId(1);
		System.out.println("下单");

		ctx.publishEvent(new OrderEvent(order,"减库存"));
		System.out.println("日志...");
	}
}

运行结果:
在这里插入图片描述
监听器有两种实现,基于接口的和基于注解的
spring的事件监听有三个部分组成:

  • 事件(ApplicationEvent) 负责对应相应监听器 事件源发生某事件是特定事件监听器被触发的原因。

  • 监听器(ApplicationListener) 对应于观察者模式中的观察者。监听器监听特定事件,并在内部定义了事件发生后的响应逻辑。

  • 事件发布器(ApplicationEventMulticaster )对应于观察者模式中的被观察者/主题, 负责通知观察者

    对外提供发布事件和增删事件监听器的接口,维护事件和事件监听器之间的映射关系,并在事件发生时负责通知相关监听器。
    在这里插入图片描述
    观察者模式
    在这里插入图片描述

Spring事件源码解析

//1:准备刷新上下文环境
prepareRefresh();

主要是把active设置为true,激活状态,closed设置为false,代表容器还没关闭,已经激活。也就是说必须要激活,才能getBean
在这里插入图片描述

protected void assertBeanFactoryActive() {
		if (!this.active.get()) {
			if (this.closed.get()) {
				throw new IllegalStateException(getDisplayName() + " has been closed already");
			}
			else {
				throw new IllegalStateException(getDisplayName() + " has not been refreshed yet");
			}
		}
	}

如果当前bean工厂没有激活,会报异常。所以当调用getBean时,容器还没有被激活是不能get的。激活才能getBean。

对bean工厂进行填充属性prepareBeanFactory(beanFactory);

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		//设置bean工厂的类加载器为当前application应用的加载器
		beanFactory.setBeanClassLoader(getClassLoader());
		//为bean工厂设置我们标准的SPEL表达式解析器对象StandardBeanExpressionResolver
		beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
		//为我们的bean工厂设置了一个propertityEditor 属性资源编辑器对象(用于后面的给bean对象赋值使用)
		beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

		//注册了一个完整的ApplicationContextAwareProcessor 
		//后置处理器用来处理ApplicationContextAware接口的回调方法
		beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
		

在bean的初始化完成后,会调用一堆的aware

		/**
		 *
		 * 忽略以下接口的bean的 接口函数方法。 在populateBean时
		 * 自动装配的概念byName byType
		 * 因为以下接口都有setXXX方法, 这些方法不特殊处理将会自动注入容器中的bean
		 */
		beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
		beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
		beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
		beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
		beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
		beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
	 * 知道为什么可以
	 * @Autowired
	 * ApplicationContext  applicationContext  就是因为这里设置了
/**
		 * 当注册了依赖解析后,例如当注册了对 BeanFactory.class 的解析依赖后,
		 * 当 bean 的属性注 入的时候, 一旦检测到属性为 BeanFactory 类型便会将 beanFactory 的实例注入进去。
		 */
		beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
		beanFactory.registerResolvableDependency(ResourceLoader.class, this);
		beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
		beanFactory.registerResolvableDependency(ApplicationContext.class, this);

会解析接口方式的监听器

beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

initApplicationEventMulticaster

创建事件多播器
所有的监听器都会注册到SimpleApplicationEventMulticaster,由他负责调用事件对应的监听器,管理所有的监听器

registerListeners

把我们的事件监听器注册到多播器上
在这里插入图片描述

在这里插入图片描述
接口方式的监听就是在这里注册到多播器里的。
最后
在这里插入图片描述
接口形式的监听器注册在这里还会注册
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值