`

Toggle Annotation实现

 
阅读更多
在上一篇博客中介绍了一个从实际项目中抽取出来的小工具:Toggle(https://github.com/xianlinbox/Toggle), 这儿来记录下其整个实现过程:

Step1: 编写自己的Annotation

Java从1.5版本引入了Annotation, 其从本质上来说不带有任何逻辑,只是可以附着在Package,Class,Method,Field等上面,为这些元素提供很多附加信息。当外部工具在解析这些元素的时候,可以读取到附加的信息,然后添加相应的功能,从而实现了Annotation的功能。

JDK本身自带了几个基础的Annotation:
@Deprecated: 标明某个类或方法不再推荐使用。
@Override: 方法重载了父类的方法
@SuppressWarnings: 忽略指定的警告信息
@Target: 定义的Annotation可以附加在哪些元素上,具体有哪些元素,可以查看JDK的ElementType。
@Retention: 定义的Annotation的作用域,具体有哪些作用域,可以查看JDK的RetentionPolicy 从上面的基础Annotation的解释,
可以看出前三个在JDK中非常常见,后2个是为大家定义自己的Annotation准备的。

定义一个基本的Annotation的基本语法如下:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface ToggleDisabled {
}

然后,需要定义该Annotation的member type,即希望在该Annotation上附着哪些信息,在使用Annotation时,有2种方式传递信息给Annotation: * 在Annotation后面直接添加值,eg:
@ToggleEnabled("STAGE1")
@ToggleEnabled({"STAGE1","STAGE2"})

这种情况,在Annotation中可以直接以value()方法即可获得附着的信息,如果想支持多值,则把返回值设为数组,
String[] value();

在Annotation后面添加键值对:
@ToggleEnabled(toggleClass = "example.MyFeatureToggle",toggleNames = {"STAGE1","STAGE2"})

这种方式想要获取键值对的“值”,就需以“键”为名定义一个方法:
String toggleClass();
String[] toggleNames();
在实现该Annotation的时候,原想使用Interface作为Member Type,这样,在为Annotation附着信息的时候,我们就可以有更大的扩展性,只要继承自某接口的类都可以作为该Annotation的值,可是在JLS规定,Annotation的Member Type只支持如下的类型:
* primitive
* String
* Enum
* Class
* 以上四类元素的数组
具体为什么有这样的限制,没找到原因,猜测是因为Annotation的静态特征,要求附着在其上的信息在整个运行过程是不能改变。

因为MemberType的限制,我不得不把该Annotation的用法从直接添加值方式,改为添加键值对的方式。完整的Annotation定义代码非常简单:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface ToggleDisabled {
String toggleClass();
String[] toggleNames();
}

Step2: 编写自己的TestRule

前面提到,Annotation的功能就是为元素附着更多的信息而已,如果没有框架来解析这些信息,那么该Annotation基本上也就是废的。这个Annotation的作用是想在跑某个测试之前,能够根据设置设定Feature Toggle的状态,从而能够在一个测试类中测试Toggle On 和Toggle Off的逻辑。

Junit从4.7版本引入了TestRule, 该接口的作用是让开发人员更加方便的控制测试运行的过程,可以使用它重写关于@After,@Before等Annotation的逻辑,从而可以让该部分逻辑可以跨多个类重用。 在Toggle中,实现原理是在运行每个测试之前,读取到Step 1创建的Annotation的信息,然后根据Annotation的信息去设置Feature Toggle的状态,然后在运行测试,运行完测试之后,把Feature Toggle还原到测试之前的状态。因此,首先编写自己的TestRule:
public class ToggleRule implements TestRule {
  @Override
  public Statement apply(final Statement statement, final Description description) {
  }
statement表示即将执行的一个测试动作,Desccription表示即将运行的测试的全部信息,即Annotation的信息也可以从decription中读取出来。
 ToggleEnabled toggleEnabledAnnotation = description.getAnnotation(ToggleEnabled.class);
 ToggleDisabled toggleDisabledAnnotation = description.getAnnotation(ToggleDisabled.class);
然后,需要读取其中的信息,把ToggleEnbled的所有Toggle设置为Status On,ToggleDisabled的所有Toggle设置为Status Off,在修改Toggle的值的时候,需要先把原始的值存储在Map里,后面在还原Feature Toggle状态时需要用到。
if (enabledAnnotation != null) {
  String[] toggleNames = enabledAnnotation.toggleNames();
  String toggleClassName = enabledAnnotation.toggleClass();

  Object[] toggles = getToggleEnumObjects(toggleClassName);
  for (Object toggle : toggles) {
       addToggleIntoMapIfAbsent(originStatusMap, toggle);
      updateToggleStatus(toggleNames, (Toggle) toggle, ToggleStatus.ON);
  }
 }

设置完状态之后,调用statement的evaluate()方法,执行测试,执行完之后,根据之前Map的值,还原Feature Toggle的状态
try {
  statement.evaluate();
} finally {
  rollBackToggleStatusToOrigin(originStatusMap, toggleEnabledAnnotation, toggleDisabledAnnotation);
}
这样,就可以通过在测试类中添加TestRule和Anotation来测试不同Toggle状态下的系统行为了。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics