前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring IoC 容器事件

Spring IoC 容器事件

作者头像
李鸿坤
发布2020-07-20 09:48:47
5600
发布2020-07-20 09:48:47
举报
文章被收录于专栏:泛泛聊后端

Spring容器除了提供Bean的生命周期扩展点,还需要提供容器的生命周期扩展点。容器不像bean一样是由开发者定义的。框架代码编写的时候并不知道谁会关心。所以Spring采用的方式是将容器的生命周期通过事件机制发布出来,关心事件的开发者自行订阅。这是一个观察者模式的典型应用。

场景

看一个简单的场景。有动态配置A,当它发生变化的时候需要按配置重新组织一个责任链的顺序,如果配置错误则不生效,使用上一次的配置并且告警。

代码语言:javascript
复制
public class EhanceFilterChain implements ApplicationContextAware {
  
  private ApplicationContext applicationContext;
  
  public List<IFilter> filterList;
  
  /**
   *  filter顺序的动态配置
   **/
  @DynamicValue(key="ehance_filter_chain_filters")
  public String filterListConfig;
  
  /**
   *  动态配置变化监听函数
   **/
  @DynamicValueListerner(key="ehance_filter_chain_filters")
  public void orderFilterList(){
    // 动态配置变更,组织责任链。如果配置错误则告警
    List<IFilter> filterListNew = new ArrayList<IFilter>();
    try{
      for(...){
               filterListNew.add(applicationContext.getBean("filterBeanName"));
            }
            this.filterList = filterListNew;
    }
    catch(Exception ex){
      ///  monitor alert
    }
    
  }
  
  @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
  
}

那么问题来了,如果配置错误,还没处理前,应用被重启,处理错误导致责任链为空,容器正常启动,服务正常暴露出去。在关键业务这种情况是不允许的。启动中如果解析错误则需要让服务停止对外暴露。那么应该如何判断容器是否是启动或者bean刷新呢 ?

解决

Spring容器提供了容器的事件机制,能够监听容器的生命周期事件。只需要实现ApplicationListener接口。

代码语言:javascript
复制
public class EhanceFilterChain implements ApplicationListener<ContextRefreshedEvent> {
  // 省略其他
  @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        ApplicationContext context = contextRefreshedEvent.getApplicationContext();
        if(context.getParent() == null){
           List<IFilter> filterListNew = new ArrayList<IFilter>();
            try{
                for(...){
                   filterListNew.add(applicationContext.getBean("filterBeanName"));
                }
                this.filterList = filterListNew;
            }
            catch(Exception ex){
              // 关闭服务
                context.close();
            }
        }
    }
}
容器事件

Spring容器事件是一个典型的观察者模式,它提供了一种容器的扩展机制。内置的容器事件有ContextRefreshedEvent ContextStartedEvent ContextStoppedEvent ContextClosedEvent

分别对应着容器生命周期的 refresh、start、stop、和close方法。同时它还提供了开发者自定义事件的方法。

首先需要定义一个事件,继承自ApplicationEvent

代码语言:javascript
复制
public class BlackListEvent extends ApplicationEvent {

    private final String address;
    private final String content;

    public BlackListEvent(Object source, String address, String content) {
        super(source);
        this.address = address;
        this.content = content;
    }

    // accessor and other methods...
}

接着定义一个被观察者,它需要有一个ApplicationEventPublisher来发布事件,容器提供了一个接口ApplicationEventPublisherAware来注入ApplicationEventPublisher。而ApplicationEventPublisher使用publishEvent方法发布事件。

代码语言:javascript
复制
public class EmailService implements ApplicationEventPublisherAware {

    private List<String> blackList;
    private ApplicationEventPublisher publisher;

    public void setBlackList(List<String> blackList) {
        this.blackList = blackList;
    }

    public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }

    public void sendEmail(String address, String content) {
        if (blackList.contains(address)) {
            publisher.publishEvent(new BlackListEvent(this, address, content));
            return;
        }
        // send email...
    }
}

事件能够发布以后,就需要实现一个或者多个观察者。它们都需要实现ApplicationListener接口,在收到事件后进行对应的处理。

代码语言:javascript
复制
public class BlackListNotifier implements ApplicationListener<BlackListEvent> {

    private String notificationAddress;

    public void setNotificationAddress(String notificationAddress) {
        this.notificationAddress = notificationAddress;
    }

    public void onApplicationEvent(BlackListEvent event) {
        // notify appropriate parties via notificationAddress...
    }
}

其他的事情是由Spring容器代劳的,开发者只需要关心事件的发布和订阅。并且两种动作是可以解耦的。发布者并不知道订阅者的存在。

缺点

容器事件可以做到设计的解耦,但是通知范围仅仅存在于容器内部,或者说是单进程内。它只能做代码级别的解耦。在分布式环境中用处并不大。分布式环境下的事件通知还是要使用消息队列中间件。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-06-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 泛泛聊后端 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 场景
  • 解决
  • 容器事件
  • 缺点
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档