Forest Forest
💒 首页
  • v1.5.30
  • v1.5.28
  • 🎄 ForestX
🌰 案例
💖 支持
🛫 更新记录
🧢 开发团队
⚒️ 参与贡献
  • MaxKey - 业界领先的身份管理和认证产品 (opens new window)
  • Snowy - 国内首个国密前后端分离快速开发平台 (opens new window)
  • Eoapi - 一个开源、可拓展的 API 工具平台 (opens new window)
  • Gitee (opens new window)
  • Github (opens new window)
💒 首页
  • v1.5.30
  • v1.5.28
  • 🎄 ForestX
🌰 案例
💖 支持
🛫 更新记录
🧢 开发团队
⚒️ 参与贡献
  • MaxKey - 业界领先的身份管理和认证产品 (opens new window)
  • Snowy - 国内首个国密前后端分离快速开发平台 (opens new window)
  • Eoapi - 一个开源、可拓展的 API 工具平台 (opens new window)
  • Gitee (opens new window)
  • Github (opens new window)
  • 序言

    • 🎁 新手介绍
    • 📖 文档
    • 🌰 使用案例
    • 🕵️‍ 关于作者
    • 👨‍🎓 贡献者列表
  • 入门

    • 🎬 安装配置说明
    • 🏹 Springboot环境安装
    • 📐 Springboot环境配置
    • 🎯 Springboot环境使用
    • 🏹 Spring环境安装
    • 📐 Spring环境配置
    • 🎯 Spring环境使用
    • 🏹 原生Java环境安装
    • 📐 原生Java环境配置
    • 🎯 原生Java环境使用
    • 🧬 编程式接口
  • 配置项

    • 👜 Springboot环境配置项
    • 👝 Spring环境配置项
    • 🎒 原生Java环境配置项
    • 📚 配置优先级/作用域
  • 声明式接口

    • 🧱 构建接口
    • 🍀 请求方法
    • 🚚 请求地址
    • 🎈 URL 参数
    • 🍭 请求头
    • 👔 请求体
    • 🍮 后端框架
    • 🧁 接口注解
    • 📬 接收数据
    • 🍛 数据转换
    • 🍓 成功/失败条件
    • 🍌 重试机制
    • 🥂 重定向
    • 🍔 Gzip解压
    • 🎂 日志管理
    • ⚽ 回调函数
    • 🍟 异步请求
    • 🛡️ HTTPS
    • 🍪 使用Cookie
    • 🛸 使用代理
    • 🍉 上传下载
    • 🚑 异常处理
  • 编程式接口

    • 请求API

      • 🚀 请求对象
      • 🚢 请求属性
      • ✨ 执行请求
      • 🎊 后端框架
      • 🎪 请求类型
      • 🔮 请求地址
      • 🧀 URL 参数
      • 🚅 请求头
      • 🚋 请求体
      • ⚓ 回调函数
      • 🚁 异步请求
      • 🥯 Cookie
      • 🍜 成功/失败条件
      • 🌶️ 重试机制
      • ⛵ 重定向
      • 🛰️ 请求代理
    • 响应API

      • 🌠 响应对象
      • ✒️ 读取数据
      • 🦋 响应状态码
      • 🏥 响应错误处理
      • 🎧 响应头
      • 🥞 Cookie
  • 模板表达式

    • 🍬 Hello World
    • 🍹 配置属性引用
    • 🍖 变量引用
    • 🥃 动态变量绑定
    • 🥗 参数序号引用
    • 🍍 引用对象属性
    • 🥝 调用对象方法
  • 高级特性

    • 🥪 拦截器
    • 🍏 自定义注解
      • 组合注解
    • 🍇 组合注解
    • 🥑 自定义转换器
  • v1.5.28文档
  • 高级特性
公子骏
2022-07-01
目录

🍏 自定义注解

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

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

# 定义一个注解

/**
 * 用Forest自定义注解实现一个自定义的签名加密注解
 * 凡用此接口修饰的方法或接口,其对应的所有请求都会执行自定义的签名加密过程
 * 而自定义的签名加密过程,由这里的@MethodLifeCycle注解指定的生命周期类进行处理
 * 可以将此注解用在接口类和方法上
 */
@Documented
/** 重点: @MethodLifeCycle注解指定该注解的生命周期类*/
@MethodLifeCycle(MyAuthLifeCycle.class)
@RequestAttributes
@Retention(RetentionPolicy.RUNTIME)
/** 指定该注解可用于类上或方法上 */
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface MyAuth {

    /**
     * 自定义注解的属性:用户名
     * 所有自定注解的属性可以在生命周期类中被获取到
     */
    String username();

    /**
     * 自定义注解的属性:密码
     * 所有自定注解的属性可以在生命周期类中被获取到
     */
    String password();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

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

# 生命周期类

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

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

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


/**
 *  MyAuthLifeCycle 为自定义的 @MyAuth 注解的生命周期类
 * 因为 @MyAuth 是针对每个请求方法的,所以它实现自 MethodAnnotationLifeCycle 接口
 * MethodAnnotationLifeCycle 接口带有泛型参数
 * 第一个泛型参数是该生命周期类绑定的注解类型
 * 第二个泛型参数为请求方法返回的数据类型,为了尽可能适应多的不同方法的返回类型,这里使用 Object
 */
public class MyAuthLifeCycle implements MethodAnnotationLifeCycle<MyAuth, Object> {


    /**
     * 当方法调用时调用此方法,此时还没有执行请求发送
     * 此方法可以获得请求对应的方法调用信息,以及动态传入的方法调用参数列表
     */
    @Override
    public void onInvokeMethod(ForestRequest request, ForestMethod method, Object[] args) {
        System.out.println("Invoke Method '" + method.getMethodName() + "' Arguments: " + args);
    }

    /**
     * 发送请求前执行此方法,同拦截器中的一样
     */
    @Override
    public boolean beforeExecute(ForestRequest request) {
        // 通过getAttribute方法获取自定义注解中的属性值
        // getAttribute第一个参数为request对象,第二个参数为自定义注解中的属性名
        String username = (String) getAttribute(request, "username");
        String password = (String) getAttribute(request, "password");
        // 使用Base64进行加密
        String basic = "MyAuth " + Base64Utils.encode("{" + username + ":" + password + "}");
        // 调用addHeader方法将加密结构加到请求头MyAuthorization中
        request.addHeader("MyAuthorization", basic);
        return true;
    }

    /**
     * 此方法在请求方法初始化的时候被调用
     */
    @Override
    public void onMethodInitialized(ForestMethod method, BasicAuth annotation) {
        System.out.println("Method '" + method.getMethodName() + "' Initialized, Arguments: " + args);
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

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

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

1
2
3
4
5
6
7
8
9
10
11
12

# 组合注解

Forest 的自定义注解功能强大,能将复杂的生命周期封装成一个优雅的注解

但每次都需要定义一个生命周期类也十分麻烦,有时候想定义的注解功能十分简单

(比如将 @Headers("Content-Type: application/json") 封装成 @MyHeaders)

这时候再定义一个类来处理此事就显得很不划算

但其实 Forest 提供了一个更简单更优雅的办法来做此类事情,那就是《组合注解》

帮助我们改善此文档 (opens new window)
上次更新: 2023/03/07, 12:59:48
🥪 拦截器
🍇 组合注解

← 🥪 拦截器 🍇 组合注解→

Theme by Vdoing | Copyright © 2016-2023 公子骏 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式