idtitlesidebar_label
custom_annotation
自定义注解
自定义注解

Forest提供了很多内置的注解,比如 @Request, @Get, @DownloadFile 等等。Forest对于请求接口的构建也是基于这些注解来工作的, 那么总有一些需求是光靠这些内置注解是满足不了的,比如公司内部定义的加签加密方式,自定义数据转换类型等等。当然,您也可以通过拦截器来做,但每个接口类或者每个方法上加上一长串类名总觉得不那么优雅,而且无法通过方法动态传入参数。 为了克服拦截器的这些缺点,自定义注解就应运而生了,以便于您可以简单优雅得解决上述各种需求,而且极大得扩展了Forest的能力。

自定义注解在技术结构上基于拦截器,本质上就是把拦截器封装成了一个个注解,所以如果还不知道拦截器是啥的话,请先看拦截器。 Forest中的所有内置注解也都是通过这样方式工作的。

定义一个注解

  1. /**
  2. * 用Forest自定义注解实现一个自定义的签名加密注解
  3. * 凡用此接口修饰的方法或接口,其对应的所有请求都会执行自定义的签名加密过程
  4. * 而自定义的签名加密过程,由这里的@MethodLifeCycle注解指定的生命周期类进行处理
  5. * 可以将此注解用在接口类和方法上
  6. */
  7. @Documented
  8. /** 重点: @MethodLifeCycle注解指定该注解的生命周期类*/
  9. @MethodLifeCycle(MyAuthLifeCycle.class)
  10. @RequestAttributes
  11. @Retention(RetentionPolicy.RUNTIME)
  12. /** 指定该注解可用于类上或方法上 */
  13. @Target({ElementType.TYPE, ElementType.METHOD})
  14. public @interface MyAuth {
  15. /**
  16. * 自定义注解的属性:用户名
  17. * 所有自定注解的属性可以在生命周期类中被获取到
  18. */
  19. String username();
  20. /**
  21. * 自定义注解的属性:密码
  22. * 所有自定注解的属性可以在生命周期类中被获取到
  23. */
  24. String password();
  25. }

上面的代码定义了一个名叫 @MyAuth 的注解,在这个注解又有一个叫 @MethodLifeCycle 的注解,这个注解的value参数指定了该注解所绑定的接口或方法的对应请求,所要执行的生命周期类。

生命周期类

生命周期类,就如其名字所暗示的一样,它包括了一个请求从方法调用、创建请求、执行完成、成功、失败等生命周期中各个环节所要经历的整个过程。

当然,您也可以理解为另一种拦截器,事实上三种生命周期接口BaseAnnotationLifeCycleMethodAnnotationLifeCycleParameterAnnotationLifeCycle 都继承自 Interceptor 接口。

好了,来看下为我们 @MyAuth 注解写的生命周期类

  1. /**
  2. * MyAuthLifeCycle 为自定义的 @MyAuth 注解的生命周期类
  3. * 因为 @MyAuth 是针对每个请求方法的,所以它实现自 MethodAnnotationLifeCycle 接口
  4. * MethodAnnotationLifeCycle 接口带有泛型参数
  5. * 第一个泛型参数是该生命周期类绑定的注解类型
  6. * 第二个泛型参数为请求方法返回的数据类型,为了尽可能适应多的不同方法的返回类型,这里使用 Object
  7. */
  8. public class MyAuthLifeCycle implements MethodAnnotationLifeCycle<MyAuth, Object> {
  9. /**
  10. * 当方法调用时调用此方法,此时还没有执行请求发送
  11. * 此方法可以获得请求对应的方法调用信息,以及动态传入的方法调用参数列表
  12. */
  13. @Override
  14. public void onInvokeMethod(ForestRequest request, ForestMethod method, Object[] args) {
  15. System.out.println("Invoke Method '" + method.getMethodName() + "' Arguments: " + args);
  16. }
  17. /**
  18. * 发送请求前执行此方法,同拦截器中的一样
  19. */
  20. @Override
  21. public boolean beforeExecute(ForestRequest request) {
  22. // 通过getAttribute方法获取自定义注解中的属性值
  23. // getAttribute第一个参数为request对象,第二个参数为自定义注解中的属性名
  24. String username = (String) getAttribute(request, "username");
  25. String password = (String) getAttribute(request, "password");
  26. // 使用Base64进行加密
  27. String basic = "MyAuth " + Base64Utils.encode("{" + username + ":" + password + "}");
  28. // 调用addHeader方法将加密结构加到请求头MyAuthorization中
  29. request.addHeader("MyAuthorization", basic);
  30. return true;
  31. }
  32. /**
  33. * 此方法在请求方法初始化的时候被调用
  34. */
  35. @Override
  36. public void onMethodInitialized(ForestMethod method, BasicAuth annotation) {
  37. System.out.println("Method '" + method.getMethodName() + "' Initialized, Arguments: " + args);
  38. }
  39. }

OK, 定义部分基本完成了。就让我们来使用刚刚定义好的 @MyAuth 注解吧

  1. /**
  2. * 在请求接口上加上自定义的 @MyAuth 注解
  3. * 注解的参数可以是字符串模板,通过方法调用的时候动态传入
  4. * 也可以是写死的字符串
  5. */
  6. @Get(
  7. url = "http://localhost:8080/hello/user?username=${username}",
  8. headers = {"Accept:text/plain"}
  9. )
  10. @MyAuth(username = "${username}", password = "bar")
  11. String send(@DataVariable("username") String username);