前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[享学Netflix] 三、Apache Commons Configuration2.x全新的事件-监听机制

[享学Netflix] 三、Apache Commons Configuration2.x全新的事件-监听机制

作者头像
YourBatman
发布2020-03-18 19:29:38
9660
发布2020-03-18 19:29:38
举报
文章被收录于专栏:BAT的乌托邦BAT的乌托邦

写好代码是个人素养,不是老板要求,更不是为了秀给同事看 代码下载地址:https://github.com/f641385712/netflix-learning

目录
  • 前言
  • 正文
    • Event
      • ConfigurationEvent
        • ConfigurationErrorEvent
      • ReloadingEvent
      • ConfigurationBuilderEvent
        • ConfigurationBuilderResultCreatedEvent
    • EventType
    • EventListener
      • AutoSaveListener
      • ReloadingBuilderSupportListener
    • EventListenerRegistrationData
    • EventListenerList
    • 使用示例
      • 使用场景

  • 总结
    • 声明

前言

前面文章重点介绍了Apache Commons Configuration1.x的使用以及原理,作为2013就已经停更的技术,本确实没有太大必要再去学它,但就因为Netflix一直还依赖它,所以这就变成了有必要。

然而作为当下的主流的2.x版本,自然也不能忽略。它几乎完全重写了1.x的代码,所以自然是不向下兼容的,并且因为包名都不一样,所以2.x和1.x是可以共存的

由于在实际使用中,那是100%推荐使用2.x版本,因此花点时间精力去了解它就变得更加具有现实意义了。本篇文章将以事件-监听机制为切入点,介绍Apache Commons Configuration2.x全新的事件-监听机制。


正文

2.x完全推翻了1.x对事件-监听机制,重新设计了一套全新API。可能它学习了Spring,使得它和Spring的事件机制颇有几分相似之处,所以理解起来对读者来说会更加亲切。


Event

继承自JDK标准事件java.util.EventObject。是所有事件的基类,同Spring的org.springframework.context.ApplicationEvent

代码语言:javascript
复制
public class Event extends EventObject {

	// root event。所以事件类型都直接/间接的继承自它
	// 如果你监听的是这种事件类型:那就是监听所有的事件
	public static final EventType<Event> ANY = new EventType<>(null, "ANY");

	// 唯一构造器  source和EventType是必须的,确定了你发送的事件源 和类型
	// 比如对Person的新增。person是事件源,新增是事件类型
	public Event(final Object source, final EventType<? extends Event> evType) { ... }
	
}

内置如下几个事件:

在这里插入图片描述
在这里插入图片描述

ConfigurationEvent

不解释。

代码语言:javascript
复制
public class ConfigurationEvent extends Event {

	// 该事件源内置的事件类型们,是public的哦
	public static final EventType<ConfigurationEvent> ANY = new EventType<>(Event.ANY, "CONFIGURATION_UPDATE");
	public static final EventType<ConfigurationEvent> ADD_PROPERTY = new EventType<>(ANY, "ADD_PROPERTY");
	... // 增删改等非常多的操作
    private final String propertyName;
    private final Object propertyValue;
    private final boolean beforeUpdate;
    ...
}

ConfigurationErrorEvent

它不再像1.x一样继承自ConfigurationEvent,而是直接继承自Event

代码语言:javascript
复制
public class ConfigurationEvent extends Event {

    public static final EventType<ConfigurationEvent> ANY = new EventType<>(Event.ANY, "CONFIGURATION_UPDATE");
    public static final EventType<ConfigurationEvent> ADD_PROPERTY = new EventType<>(ANY, "ADD_PROPERTY");
    ... // 省略其它事件类型
    
    private final String propertyName;
    private final Object propertyValue;
    private final boolean beforeUpdate;
	...
}

ReloadingEvent

Reloading:重新加载,它在Commons Configuration了是个很重要的概念,一般服务于热更新、热加载等重要功能。 这个事件比较特殊,可重点关注下。

代码语言:javascript
复制
public class ReloadingEvent extends Event {

	// 注意:它仅有这一个事件类型,木有子类型
    public static final EventType<ReloadingEvent> ANY = new EventType<>(Event.ANY, "RELOAD");
    
    // reloading时发送的额外数据,没有可为null
    private final Object data;

	// 小细节:从这个方法可以得出结论
	// 发送这个事件的事件源:一定是ReloadingController
    public ReloadingController getController() {
        return (ReloadingController) getSource();
    }
}

发送这个事件的事件源:一定是ReloadingController


ConfigurationBuilderEvent

它也是一个很特殊的事件,该事件会在ConfigurationBuilder里发出。

代码语言:javascript
复制
public class ConfigurationBuilderEvent extends Event {

    public static final EventType<ConfigurationBuilderEvent> ANY = new EventType<>(Event.ANY, "BUILDER");
	
	// 在BasicConfigurationBuilder#resetResult()方法被调用的时候,此事件发出
    public static final EventType<ConfigurationBuilderEvent> RESET = new EventType<>(ANY, "RESET");
	// ConfigurationBuilder#getConfiguration()被调用时候,获取时。发送这个事件
	// 当时请注意:这这是发起了request,但不一定get成功了(中途可能抛出异常嘛)
    public static final EventType<ConfigurationBuilderEvent> CONFIGURATION_REQUEST = new EventType<>(ANY, "CONFIGURATION_REQUEST");


	// 同样的,这个事件源必须是`ConfigurationBuilder`
    @Override
    public ConfigurationBuilder<?> getSource() {
        return (ConfigurationBuilder<?>) super.getSource();
    }
}

它的事件源一定是一个ConfigurationBuilder实例。


ConfigurationBuilderResultCreatedEvent

它对父类ConfigurationBuilderEvent扩展,增加事件类型RESULT_CREATED,表示Result创建成功后的事件(注意和父类的CONFIGURATION_REQUEST的区别哦)。

并且该事件源还提供了对Configuration的访问方法,因为发送该时间肯定能确定Configuration实例已经创建成功了嘛~~~

代码语言:javascript
复制
public class ConfigurationBuilderResultCreatedEvent extends ConfigurationBuilderEvent {

	// 它表示Result获取**成功**后,发送的事件
    public static final EventType<ConfigurationBuilderResultCreatedEvent> RESULT_CREATED = new EventType<>(ANY, "RESULT_CREATED");

	// 提供对配置对象的访问,毕竟发送此事件代表肯定创建成功了嘛~
	// 绝大部分情况是Configuration
	private final ImmutableConfiguration configuration;
}

EventType

事件类型。每个类型可以有它属于的父类型,以及名称。Spring里并没有提出时间类型的概念,而是通过Class类型去区分,这一点上我倒觉得Commons Configuration更有优势些~

代码语言:javascript
复制
public class EventType<T extends Event> implements Serializable {

	private final EventType<? super T> superType;
	private final String name;
	
	... // 省略构造器

	// 获取到该类型所有的父类型(递归调用到顶层)
	public static Set<EventType<?>> fetchSuperEventTypes(final EventType<?> eventType) { ... }
	// derivedType是否是baseType的子类型(递归去比较)
	public static boolean isInstanceOf(final EventType<?> derivedType, final EventType<?> baseType) { ... }
}

绝大多数情况下,你并不需要去自定义自己的EventType,使用现成的即可。


EventListener

监听器,监听指定的事件(类型),它是个函数式接口。可类比org.springframework.context.ApplicationListener,它也是个函数式接口,接口方法叫:onApplicationEvent(E event)

代码语言:javascript
复制
public interface EventListener<T extends Event> {
	void onEvent(T event);
}

列出几个内置常用实现:


AutoSaveListener

实现基于文件自动保存机制的侦听器类配置,同时它也实现了FileHandlerListener从而可以让文件自动保存。它的访问权限是default,外部并不能直接使用它。


ReloadingBuilderSupportListener

一个内部使用的帮助类,用于向任意Configuration对象添加Reloading支持:这种支持包括自动resetResult()resetReloadingState()重置状态,所以是非常有必要的。

代码语言:javascript
复制
// 说明:它监听的是所有事件哦~~~~
final class ReloadingBuilderSupportListener implements EventListener<Event> {
	private final BasicConfigurationBuilder<?> builder;
	private final ReloadingController reloadingController;
	... // 省略构造器赋值

	// 当监听的事件是:RESULT_CREATED,也就是Configuration被成功创建return出去后,给重置其ReloadingState
	// 也就是说保证获取出去的对象仍可以热加载..

	// 而如果是其它事件,resetResult() -> result =null并且发出RESET事件
    @Override
    public void onEvent(final Event event) {
        if (ConfigurationBuilderResultCreatedEvent.RESULT_CREATED.equals(event.getEventType())) {
            reloadingController.resetReloadingState();
        } else {
            builder.resetResult();
        }
    }

	// =======同时它还提供一个静态方法,方便你绑定======
    public static ReloadingBuilderSupportListener connect(
            final BasicConfigurationBuilder<?> configBuilder,
            final ReloadingController controller) {
            
		final ReloadingBuilderSupportListener listener = new ReloadingBuilderSupportListener(configBuilder, controller);
		// 把该时间绑定在ReloadingController上
		// 说明:ReloadingController它自己是个EventSource哦,所以可以监听它
		// 并且它可以发出ReloadingEvent事件出来。所以发现,当发出ReloadingEvent出来时,触发本监听器的resetResult操作
		controller.addEventListener(ReloadingEvent.ANY, listener);

		// 给builder监听上RESULT_CREATED这个事件,当RESULT_CREATED到达此监听器时候会清空Result
		configBuilder.installEventListener( ConfigurationBuilderResultCreatedEvent.RESULT_CREATED, listener);

		return listener;
	}
}

该监听器对外暴露的方法其实只有:ReloadingBuilderSupportListener.connect(this, controller);,在BasicConfigurationBuilder创建ReloadingController的时候会用到。

EventListenerRegistrationData

Registration:注册,登记。包含事件侦听器注册信息的数据类

代码语言:javascript
复制
// 为注册监听器时提供数据
public final class EventListenerRegistrationData<T extends Event> {
	private final EventType<T> eventType;
	private final EventListener<? super T> listener;
}

EventListenerList

这个就比较简单了,内部管理维护着一堆监听器,并且提供注册、移除、清空、获取等方法。

代码语言:javascript
复制
public class EventListenerList {
	private final List<EventListenerRegistrationData<?>> listeners;
	// 可以看到对它的增删改都是线程安全的
    public EventListenerList() {
        listeners = new CopyOnWriteArrayList<>();
    }
	... //addEventListener/removeEventListener/fire/getEventListeners


	// 拿到监听此事件类型(以及其所有子事件类型)所有的监听器们
	public <T extends Event> List<EventListenerRegistrationData<? extends T>> getRegistrationsForSuperType(EventType<T> eventType) { ... }
	...
}

使用示例

介绍了这么多,从API层面应该能深刻感受到它和1.x的设计完全是两码事里,反倒和Spring的设计几乎如出一辙。

那么在使用层面是否友好呢?请看下面这个简单案例:

代码语言:javascript
复制
@Test
public void fun100() throws ConfigurationException {
    Configurations configs = new Configurations();
    PropertiesConfiguration config = configs.properties("1.properties");

    // 监听ADD_PROPERTY添加属性事件
    config.addEventListener(ConfigurationEvent.ADD_PROPERTY, event -> {
        if (!event.isBeforeUpdate()) {
            System.out.printf("成功添加属性:%s = %s", event.getPropertyName(), event.getPropertyValue());
        }
    });

    config.addProperty("name","YourBatman");
}

控制台打印:

代码语言:javascript
复制
成功添加属性:name = YourBatman

可以看到,核心API虽然变化极大,但在最基础的使用层面其实变化并不太大,对使用者相对友好。但话说回来,还是有不小切换、以及理解成本的。


使用场景

监听器的典型使用场景:记录配置文件的修改记录


总结

关于Apache Commons Configuration2.x版本的事件-监听机制就介绍到这了,以它为例可以看到2.x相较于1.x的改动是非常之大的,这就是为何Apache团队不在1.x上直接升级而选择重新命名的最重要的原因了吧。

以点见面,2.x各个部分改动均不小,所以从1.x的知识迁移到2.x并不会很平滑,甚至需要重新学习,本系列也会逐渐把它展示在大家面前,以便工作中自由的使用Apache Commons Configuration2.x版本

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 目录
  • 前言
  • 正文
    • Event
      • ConfigurationEvent
      • ReloadingEvent
      • ConfigurationBuilderEvent
    • EventType
      • EventListener
        • AutoSaveListener
        • ReloadingBuilderSupportListener
      • EventListenerRegistrationData
        • EventListenerList
          • 使用示例
          • 总结
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档