关于监听器
nacos也是利用监听器来进行集成的,所以还需要学习原理
要创建监听器需要俩个东西
- 事件
- 监听器
来个简单例子来看看
自定义事件
/***
* 事件
*/
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 |
重点了解ContextRefreshedEvent
和ContextClosedEvent
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
把我们的事件监听器注册到多播器上
接口方式的监听就是在这里注册到多播器里的。
最后
接口形式的监听器注册在这里还会注册