# 依赖注入
> 从 `1.0-BETA`版本开始增加依赖注入功能。
依赖注入,简单来讲,就是你提供一个对象给我来保管,当你需要的时候我便会给你。这个功能就类似于`Spring`框架中的`@Bean`、`@Component` 这类注解。
首先,我们也是使用注解来进行依赖注入的,并为了以防万一,也提供了代码形式的注入方式。
> *或许这就是所谓的 Ioc*
首先先不提特殊情况,讲讲正常情况下的注入方式。
> 为了防止注解名称冲突,特意取了与Spring框架中的注解名不同的名称, 还请不要弄混了
> **依赖注入的大前提:所有注解均在包扫描范围内。**
## @Beans
> 全包路径:`om.forte.qqrobot.anno.depend.Beans`
* ### 参数:
| 参数名 | 参数类型 | 默认值 | 备注 |
| --- | --- | --- | --- |
| `value` | `String` | `""` | 依赖对象的名称,如果没有则以类名取代 |
| `single` | `boolean` | `true` | 是否为单例,默认为单例 |
| `constructor` | `Class[]` | `{}` | 根据参数类型列表来指定构造函数,默认为无参构造。***仅在注解使用在类上时生效*** |
| `allDepend` | `boolean` | `false` | 是否将类中全部字段标记为Depend,默认为false。**`v1.1-BETA`后添加** |
| `depend` | `Depend` | `@Depend` | 当全部标记为@Depend的时候,此参数为所有字段标记的@Depend注解对象,默认为无参注解。**`v1.1-BETA`后添加** |
| `init` | `boolean` | `false` | 是否在依赖注入流程结束后初始化一次,默认为false,即使用懒加载策略。此初始化操作由核心启动器控制。 **`v1.12.0`后添加** |
| `priority` | `int` | PriorityConstant.FIRST_LAST (即优先级最低) | 优先级。当在获取某个依赖的时候,假如在通过类型获取的时候存在多个值,会获取优先级更高级别的依赖并摒弃其他依赖。使用jdk自带的排序规则,即升序排序。默认为优先级最低。 **`v1.12.0`后添加** |
> 上述中,参数`priority`的默认值`PriorityConstant`是核心`v1.12.0`后追加的常量类。参考文档章节`通用API与功能` -> `常量`
此注解便是标注需要进行依赖管理的注解。此注解可以使用在类上或方法上。
* ### 使用在类上
当使用在类上的时候,便代表将此类作为一个依赖对象保存。依赖管理器会根据此类构建实例化函数并保存。
关于对依赖的实例化,依赖管理器会优先使用`@Constr`注解所标注的静态方法,其次使用注解中的`constructor`参数。
> 关于`@Constr`注解的讲解可以查阅`监听消息`章节
* ### 使用在方法上
***当使用在方法上的时候,此方法所在的类上也必须存在@Beans注解才可以。***
***此方法必须有返回值。***
假如此方法有参数,依赖管理器会根据存入的依赖进行自动注入,当然,你也可以使用`@Depend`注解进行手动指定。(`@Depend`注解将会在后续讲到)
> 就像春天(Spring)一样。
* ### 举个例
```java
/**
* demo, 将类MyBean作为依赖保存, 未做特殊指定则使用无参构造实例化,依赖名为'singleBean'
* @since JDK1.8
**/
@Beans("singleBean")
public class MyBean {
/** 将Bean1类型作为依赖保存,不是单例 */
@Beans(single = false)
public Bean1 getBean1(){
return new Bean1();
}
/**
* 将一个类型为Bean2的依赖保存,需要两个参数:Bean1, MyBean,依赖名为'bean2'
* 其中,Bean1使用自动注入,MyBean使用指定注入
*/
@Beans
public Bean2 getBean2(Bean1 bean1, @Depend MyBean myBean){
return new Bean2(bean1, myBean);
}
/** 将一个基础数据类型String的依赖保存,依赖名为'a' */
@Beans
public String getA(){
return "233";
}
}
```
需要注意的是,在上面的依赖中,出现了一个"基础数据类型"的依赖。
所谓基础数据类型,指的就是java中8大基础数据类型、8大基础数据类型的封装类和String类。
当在注入这些类型的依赖的时候,默认的依赖名将会根据方法名而不是类型名,在注入的时候也会根据参数名而不是类型来获取。
当注入的方法名的命名规则为getter或者setter方法的规则的时候,会自动移除get/set并尝试获取正确的依赖名。
例如:
`getA() -> a;`
`getsetA() -> getsetA;`
`getSetA() -> setA;`
> 假如使用类型来获取基础数据类型依赖的话,会报错的哦~
## @Depend
> 全包路径:`com.forte.qqrobot.anno.depend.Depend`
* ### 参数
| 参数名 | 参数类型 | 默认值 | 备注 |
| --- | --- | --- | --- |
| `value` | `String` | `""` | 依赖对象的名称,如果没有则以类名取代 |
| `type` | `class[]` | `{}` | 如果要使用字段类型进行注入,但是此类型可能存在多个(不同实现类),那么使用此参数来指定类型。(※ \*\*\*虽然类型为数组,但是仅只能使用一个类型,如果有多个则只取第一个。 \*\*\* ) |
| `nonNull` | `boolean` | `true` | 是否当字段值不为null的时候再进行注入,一般用于避免与其他依赖注入的参数出现冲突,默认为true。只有此值为true的时候 byGetter 参数才会生效 |
| `byGetter` | `boolean` | `false` | 是否使用getter方法对字段的可否注入进行判断,默认为false,即直接判断字段的值 |
| `getterName` | `String` | `""` | 如果 byGetter 为true, 则可以使用此值对getter方法的方法名进行指定。如果不指定则默认为通用的getter方法名,即get+字段名。getter方法的参数必须为空 |
| `bySetter` | `boolean` | `false` | 是否使用setter方法对字段进行赋值注入,默认为false,即直接对字段值进行操作 |
| `setterName` | `String` | `""` | 如果bySetter 为true, 则可以使用此值对setter方法的方法名进行指定。如果不指定则默认为通用的setter方法名,即set + 字段名。setter方法的参数为一个字段自身类型的参数 |
此注解可以使用在类中的字段上和方法的参数上。
* ### 使用在字段上
当使用在字段上的时候,此注解的所有的参数都是有效的、可选的。
需要注意的一点是,假如你的字段需要注入的数据类型为上文所提到的`基础数据类型`,
如果你的字段名与你想要的依赖名不符合,那么请务必指定注入的依赖名而不能通过自动判断字段名称来注入。啥?你问我为什么?很显而易见的原因我就不多阐述了。
> 大家都懂
* ### 使用在方法的参数上
当使用在方法的参数上的时候,此注解仅有`value`和`type`参数生效,毕竟参数不存在什么是否为null这类东西。
其余事项与在字段上使用相同。
不过目前来讲,只有在监听函数中使用此注解才会生效。毕竟监听函数通过框架内部的监听函数调度执行,而别的方法是你自己手动触发的。
*当然,上面提到的注入依赖的时候也会自动获取参数的。*
> 显而易见的原因
* ### 举个例
```java
/**
* 注入Bean1
* @since JDK1.8
**/
@Beans
public class Bean1 {
/** 将基础数据类型依赖'a'注入 */
@Depend
private String a;
/** 通过setter函数注入Bean2对象 */
@Depend(bySetter = true)
private Bean2 bean2;
public String getA() {
return a;
}
public void setA(String a) {
this.a = a;
}
public Bean2 getBean2() {
return bean2;
}
/** 由于字段bean2标注了bySetter,所以会打印'setter' */
public void setBean2(Bean2 bean2) {
System.out.println("setter");
this.bean2 = bean2;
}
}
```
- 前言
- 简单介绍
- 我该如何阅读文档
- 视频教程
- 安装与使用
- 开源协议
- 免责&捐助
- 注意事项
- 版本命名规则简介
- 主要功能版本历史
- 功能指引
- 通用API与功能
- 国际化语言
- 异常
- 消息监听
- @Listen
- @OnListen模板
- @Filter
- 消息参数截取
- @Spare
- @Constr
- @Ignore
- @ListenBreak
- @ListenBreakPlugin
- @ListenBody
- 监听响应
- 成功判定与返回值
- 监听上下文
- 动态参数
- 监听消息API
- 异步监听 @Async
- 限流监听 @Limit
- 自定义Http送信器
- 枚举与类型
- 送信器
- SENDER
- SETTER
- GETTER
- 返回值
- 其他位置
- 日志与日志拦截
- 异常处理
- 依赖注入
- 自定义依赖获取
- 批量依赖载入
- 注意事项
- 常量
- 定时任务
- 自定义过滤
- 拦截器
- 拦截器总定义
- 上下文对象总定义
- 监听消息拦截
- 送信器拦截
- 监听函数拦截
- CAT码
- CQ码
- CQCode
- CQ码工具类
- CQ扩展工具类
- AppendList拼接链
- 高级内容
- 阻断机制
- 截断机制
- 自定义枚举类型
- 枚举工厂
- byName注解
- 自定义注解
- 配置
- 文件配置
- 注解配置
- 参数配置
- 多配置
- 多账号
- 自定义账号管理器
- 小心!
- 核心版本与组件如何升级核心
- 核心版本迭代指南
- 核心
- 更新日志
- jar包与依赖
- 监听消息类结构图
- 快速启动
- 模组与扩展
- 模组开发
- 现有模组
- 通用模组-延时任务
- 通用模组-CQ码工具
- 转义器
- CQ码操作工具类
- CQ码模板-CodeTemplate
- CQ码载体-KQCode
- CQ码构建器
- MQ码工具类
- 通用模组-redis-bot管理器
- 通用模组-Debugger
- Debugger-common模块
- Debugger-server模块
- Debugger-client模块
- 通用模组-钉钉机器人
- 组件-Mirai(JVM)
- 快速开始(1.13+)
- springboot-starter
- 注意事项
- 配置
- 额外的内容
- 快速回复
- 额外监听
- CQ码解析
- 组件-酷Q(QQ)-CQ HTTP API(基本失效)
- 快速开始(推荐)
- 快速开始(1.7.x以下,不推荐)
- 快速开始(Springboot启动器)
- 启动器、启动接口与配置类
- 配置
- 文件配置
- 注意事项
- 更新日志
- 额外的内容
- 自定义额外监听
- CQ送信器
- 元事件
- 监听消息类结构图
- 组件-酷Q(QQ)-JCQ(失效)
- 快速开始
- 更新日志
- 额外的内容
- JCQ日志
- 配置
- 注意事项
- 监听消息类结构图
- 组件-酷Q(QQ)-HTTP TO CQ(失效)
- 快速开始(核心1.7.x及以下)
- 启动器、启动接口与配置类
- 配置
- 文件配置
- 注意事项
- 更新日志
- 监听消息类结构图
- 组件-酷Q(QQ)-LEMOC (失效)
- 快速开始(核心1.7.x及以下)
- 配置
- 注意事项
- 更新日志
- 监听消息类结构图
- Springboot快速启动器
- 常见问题汇总
- BUG反馈
- 更新计划总览