200行Java代码如何实现依赖注入框架详解

依赖注入介绍

先回顾下依赖注入的概念:

我们常提起的依赖注入(Dependency Injection)和控制反转(Inversion of Control)是同一个概念。具体含义是:当某个角色(可能是一个Java实例,调用者)需要另一个角色(另一个Java实例,被调用者)的协助时,在 传统的程序设计过程中,通常由调用者来创建被调用者的实例。但在Spring里,创建被调用者的工作不再由调用者来完成,因此称为控制反转;创建被调用者 实例的工作通常由Spring容器来完成,然后注入调用者,因此也称为依赖注入。

其实简单的说,依赖注入起到的作用就是讲对象之间的依赖关系从原先的代码中解耦出来,通过配置文件或注解等方式加上Spring框架的处理让我们对依赖关系灵活集中的进行管理。

依赖注入框架

依赖注入框架并不神秘,其实它是非常简单的东西。不要去看spring的依赖注入源码,因为你只要一去看就意味着你再也写不敢下手自己撸了,它的功能因为过于强大,所以设计也过于复杂,普通程序员一眼看去只能望洋兴叹。

我也并没有去细致阅读spring源码。即便如此也只用了半天的时间便自己撸了一个基本满足标准依赖注入规范「JSR-330」的小框架iockids。这个小框架只有一个主类Injector,大约200行代码,它具备以下功能。

  1. 单例/非单例注入
  2. 构造器注入
  3. 字段注入
  4. 循环依赖注入
  5. Qualifier注入

我们看一个稍微复杂一点的使用示例

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import iockids.Injector;
@Singleton
class Root {
 @Inject
 @Named("a")
 Node a;
 @Inject
 @Named("b")
 Node b;
 @Override
 public String toString() {
  return String.format("root(%s, %s)", a.name(), b.name());
 }
}

interface Node {
 String name();
}

@Singleton
@Named("a")
class NodeA implements Node {
 @Inject
 Leaf leaf;
 @Inject
 @Named("b")
 Node b;
 @Override
 public String name() {
  if (b == null)
   return String.format("nodeA(%s)", leaf);
  else
   return String.format("nodeAWithB(%s)", leaf);
 }
}

@Singleton
@Named("b")
class NodeB implements Node {
 Leaf leaf;
 @Inject
 @Named("a")
 Node a;
 @Inject
 public NodeB(Leaf leaf) {
  this.leaf = leaf;
 }

 @Override
 public String name() {
  if (a == null)
   return String.format("nodeB(%s)", leaf);
  else
   return String.format("nodeBWithA(%s)", leaf);
 }
}

class Leaf {
 @Inject
 Root root;
 int index;
 static int sequence;
 public Leaf() {
  index = sequence++;
 }

 public String toString() {
  if (root == null)
   return "leaf" + index;
  else
   return "leafwithroot" + index;
 }

}

public class Demo {
 public static void main(String[] args) {
  var injector = new Injector();
  injector.registerQualifiedClass(Node.class, NodeA.class);
  injector.registerQualifiedClass(Node.class, NodeB.class);
  var root = injector.getInstance(Root.class);
  System.out.println(root);
 }
}

上面这份代码用到了iockids提供的所有功能。

  1. Root/NodeA/NodeB类是单例类
  2. Leaf类是非单例类
  3. 它们都使用了字段注入
  4. NodeB使用了构造器注入
  5. NodeA和NodeB还使用了Qualifier名称注入
  6. Leaf类中有Root类型的字段,这便是循环依赖
  7. NodeA中有NodeB字段,NodeB中有NodeA字段,这也是循环依赖

为了便于理解上述代码,我画了依赖图

上面的代码输出如下

root(nodeAWithB(leafwithroot0), nodeBWithA(leafwithroot1))

从这个输出中,我们也可以大致想象出依赖结构。

iockids提供了丰富的注入错误异常报告,防止用户注入配置出错。

比如我们将上面的NodeA和NodeB的名称都配置成一样的a,就会曝出下面的错误堆栈

iockids.InjectException: duplicated qualifier javax.inject.Named with the same class iockids.demo.Node
 at iockids.Injector.registerQualifiedClass(Injector.java:87)
 at iockids.Injector.registerQualifiedClass(Injector.java:70)
 at iockids.demo.Demo.main(Demo.java:106)

如果我们将NodeB的构造器随意加一个参数

 @Inject
 public NodeB(Leaf leaf, int k) {
  this.leaf = leaf;
 }

运行时就会抛出下面的错误

iockids.InjectException: no accessible constructor for injection class int
 at iockids.Injector.createNew(Injector.java:120)
 at iockids.Injector.createNew(Injector.java:94)
 at iockids.Injector.createFromParameter(Injector.java:167)
 at iockids.Injector.createFromConstructor(Injector.java:145)
 at iockids.Injector.createNew(Injector.java:123)
 at iockids.Injector.createFromQualified(Injector.java:216)
 at iockids.Injector.createFromField(Injector.java:173)
 at iockids.Injector.injectMembers(Injector.java:233)
 at iockids.Injector.createNew(Injector.java:136)
 at iockids.Injector.createFromQualified(Injector.java:216)
 at iockids.Injector.createFromField(Injector.java:173)
 at iockids.Injector.injectMembers(Injector.java:233)
 at iockids.Injector.createNew(Injector.java:136)
 at iockids.Injector.createNew(Injector.java:94)
 at iockids.Injector.getInstance(Injector.java:245)
 at iockids.demo.Demo.main(Demo.java:107)

项目开源地址:https://github.com/pyloque/iockids (本地下载)

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

您可能感兴趣的文章:

  • 实例讲解Java的Spring框架中的控制反转和依赖注入
  • 深入解析Java的Spring框架中bean的依赖注入
  • 详解Java Spring各种依赖注入注解的区别
  • JavaWeb Spring依赖注入深入学习
(0)

相关推荐

  • JavaWeb Spring依赖注入深入学习

    一.依赖注入(DI) 依赖注入听起来很高深的样子,其实白话就是:给属性赋值.一共有两种方法,第一是以构造器参数的形式,另外一种就是以setting方法的形式. 1 构造器注入 1 使用构造器注入 使用xml的注入方式 A. 通过参数的顺序 <constructor-arg index="0"><value>张三</value></constructor-arg> <constructor-arg index="1"

  • 实例讲解Java的Spring框架中的控制反转和依赖注入

    近来总是接触到 IoC(Inversion of Control,控制反转).DI(Dependency Injection,依赖注入)等编程原则或者模式,而这些是著名 Java 框架 Spring.Struts 等的核心所在.针对此查了 Wikipedia 中各个条目,并从图书馆借来相关书籍,阅读后有些理解,现结合书中的讲解以及自己的加工整理如下: eg1 问题描述: 开发一个能够按照不同要求生成Excel或 PDF 格式的报表的系统,例如日报表.月报表等等.   解决方案: 根据"面向接口编

  • 详解Java Spring各种依赖注入注解的区别

    注解注入顾名思义就是通过注解来实现注入,Spring和注入相关的常见注解有Autowired.Resource.Qualifier.Service.Controller.Repository.Component. Autowired是自动注入,自动从spring的上下文找到合适的bean来注入 Resource用来指定名称注入 Qualifier和Autowired配合使用,指定bean的名称 Service,Controller,Repository分别标记类是Service层类,Contro

  • 深入解析Java的Spring框架中bean的依赖注入

    每一个基于java的应用程序都有一个共同工作来展示给用户看到的内容作为工作的应用几个对象.当编写一个复杂的Java应用程序,应用程序类应该尽可能独立其他Java类来增加重复使用这些类,并独立于其他类别的测试它们,而这样做单元测试的可能性.依赖注入(或有时称为布线)有助于粘合这些类在一起,同时保持他们的独立. 考虑有其中有一个文本编辑器组件的应用程序,要提供拼写检查.标准的代码将看起来像这样: public class TextEditor { private SpellChecker spell

  • 200行Java代码如何实现依赖注入框架详解

    依赖注入介绍 先回顾下依赖注入的概念: 我们常提起的依赖注入(Dependency Injection)和控制反转(Inversion of Control)是同一个概念.具体含义是:当某个角色(可能是一个Java实例,调用者)需要另一个角色(另一个Java实例,被调用者)的协助时,在 传统的程序设计过程中,通常由调用者来创建被调用者的实例.但在Spring里,创建被调用者的工作不再由调用者来完成,因此称为控制反转;创建被调用者 实例的工作通常由Spring容器来完成,然后注入调用者,因此也称为

  • 200行Java代码编写一个计算器程序

    发现了大学时候写的计算器小程序,还有个图形界面,能够图形化展示表达式语法树,哈哈;) 只有200行Java代码,不但能够计算加减乘除,还能够匹配小括号~ 代码点评: 从朴素的界面配色到简单易懂错误提示,无不体现了"用户体验"至上的设计理念:代码异常处理全面合理.滴水不漏,代码缩进优雅大方,变量命名直观易懂:再结合长度适中简单明了的注释,程序整体给人一种清新脱俗之感.背后不难看出作者对学习的热爱以及对设计的苛求,工匠精神可见一斑,真可谓是大学数据结构学以致用的典范! 实现代码如下所示:

  • Java spring的三种注入方式详解流程

    目录 设置Spring的作用域 自动注入 @Primary Qualifier @ComponentScan不同的配置对性能的影响 懒加载 三种注入方式 字段注入(IDEA 会提示不推荐) 字段注入的bean类外部不可见 循环依赖问题 构造器注入(官方推荐) set方法注入 设置Spring的作用域 或者使用枚举值设置 单例和多里使用场景 自动注入 @Primary 一个接口有多个实现被spring管理吗,在依赖注入式,spring会不知道注入哪个实现类就会抛出NoUniqueBeanDefin

  • Spring配置与依赖注入基础详解

    目录 1.Spring配置 1.1.别名 1.2.Bean的配置 1.3.import 2.依赖注入(DI) 2.1.构造器注入 2.2.Set 注入(重点) 2.3.扩展的注入 2.4.Bean的作用域 1.Spring配置 1.1.别名 别名 alias 设置别名 , 为bean设置别名 , 可以设置多个别名 <!--设置别名:在获取Bean的时候可以使用别名获取--> <alias name="userT" alias="userNew"/&

  • Spring quartz Job依赖注入使用详解

    Spring quartz Job依赖注入使用详解 一.问题描述: 使用Spring整合quartz实现动态任务时,想在job定时任务中使用某个service时,直接通过加注解@Component.@Autowired是不能注入的,获取的对象为Null.如下面的代码: @Component @PersistJobDataAfterExecution @DisallowConcurrentExecution public class TicketSalePriceLessThanLowestPri

  • php依赖注入知识点详解

    引言 你知道什么是依赖注入吗?依赖注入(DI)的概念虽然听起来很深奥,但是如果你用过一些新兴的php框架的话,对于DI一定不陌生,因 为它们多多少少都用到了依赖注入来处理类与类之间的依赖关系. php中传递依赖关系的三种方案 其实要理解DI,首先要明白在php中如何传递依赖关系. 第一种方案,也是最不可取的方案,就是在A类中直接用new关键词来创建一个B类,如下代码所示: <?php class A { public function __construct() { $b = new B();

  • JavaScript反射与依赖注入实例详解

    本文实例讲述了JavaScript反射与依赖注入.分享给大家供大家参考,具体如下: 对于javascript中的反射的理解,一直都是认为,利用数组对回调函数进行保存,之后在适当的时刻利用call或是apply 方法,对回调进行调用即可,一般如下操作: 首先定义两个方法: var service = function() { return { name: 'Service' }; } var router = function() { return { name: 'Router' }; } 我们

  • Spring Bean常用依赖注入方式详解

    一般而言,Spring的依赖注入有三种:构造器注入.setter注入以及接口注入.本文主要讲构造器注入与setter注入. 1.构造器注入 为了让Spring完成构造器注入,我们需要去描述具体的类.构造方法并设置构造方法的对应参数. 代码如下: public class Role { private Long id; private String roleName; private String note; public Long getId() { return id; } public vo

  • Java代码重构的几种模式详解

    Java代码的重构模式主要有三种: 重命名方法重构模式.引入解释性变量重构模式.以查询取代临时变量重构模式 重命名方法重构模式建议执行如下的步骤来完成: 1.建立一个具有新名称的方法 2.将旧方法的方法体复制进新方法 3.讲旧方法的方法体修改为调用新方法 4.将所有引用旧方法的地方修改为引用新方法 5.删除旧方法 引入解释性变量重构模式步骤相对简单,如下: 1.声明一个局部变量,并将其初始化为需要替换的表达式部分 2.对于复杂的表达式,用新的局部变量代替其中需要替换的部分 3.对于该表达式的其他

  • php中的依赖注入实例详解

    本文实例讲述了php中的依赖注入.分享给大家供大家参考,具体如下: 依赖注入是一种允许我们从硬编码的依赖中解耦出来,从而在运行时或者编译时能够修改的软件设计模式. 我到现在依然不大明白上面"依赖注入"的定义是什么-- 有兴趣可以参考下<PHP之道>上面对"依赖注入"的 解释. http://laravel-china.github.io/php-the-right-way/#dependency_injection 简而言之就是可以让我们在类的方法中更加

随机推荐