# 依赖注入 > 从 `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; } } ```