什么是DI

DI是Dependency Injection的简称,翻译过来就是“依赖注入”。熟悉java开发的同学都会接触到这个概念,在spring、guice等框架中均是一个基本概念。依赖注入所作的事情就是,在程序运行时为类的变量赋予实例。DI解决了代码分层开发,层次之间的代码解耦问题。与DI相伴生的概念是IOC,他们是同一事物的不同方面的阐述。

如何实现DI

如果我们编写的代码中,没有为类的变量创建实例,那么就要想办法在程序运行时为变量赋值。我目前能想到的方法就是利用java的reflect机制,利用反射在类加载过程中为变量赋值。对于一个项目工程,我们设计时知道哪些类的变量需要注入实例,而选择在类加载时为其创建指定的实例并赋值给特定的变量,但是这样会出现因人而异,导致代码设计的混乱,代码的质量太差而且不可复用。如何让程序自己知道哪些类的哪些变量需要进行DI,最好的方法就是做标记,java提供的Annotation技术能够很好的完成这个任务。利用注解,我们可以为变量打上标记,java程序能够识别这些注解,java的反射机制提供了获取注解的API,我们可以很好利用这点。利用java的反射和注解技术,我们可以在程序运行时对指定的package路径下的被标记的一些类进行加载,在加载过程中创建实例对象赋给被标记的变量。这种方式,将其完善就可以复用,其就形成一套框架技术。

示例

简单的代码模拟实现java的DI。

简单的注解:

@Target(ElementType.TYPE)
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface MyComponent {}

不了解java注解的同学,这里需要知道两个关键注解:

  • @Target注解表示注解的作用范围,它可以是类型、方法、字段等等,可以查看java.lang.annotation.ElementType这个API,查看其允许的所有范围。
  • @Retention注解表示注解的保留时长,它有三种策略SOURCE、CLASS、RUNTIME,分别表示:只存在于源码中,编译时丢弃;编译时保留,jvm运行时丢弃;保留至程序运行时。默认是CLASS策略。我们要利用注解,在程序运行时为变量注入实例,因此注解要保留至程序运行时,所以策略一定选择RUNTIME。

注解的使用:

//简单的接口
public interface People {public void whereIFrom();
}//简单的实现
@MyComponent
public class Chinese implements People {@Overridepublic void whereIFrom() {System.out.println("I'm from China.");}}//需要DI的地方
public class Printer {@Injectprivate People p;public void print() {p.whereIFrom();}}

从上面代码可以看到,Printer类的成员p没有创建实例,在print方法中我使用到了变量p,这里p就需要注入一个实例,程序才能正确运行。@Inject注解是JDK提供的注解,它已经被spring及guice等框架所使用。被@Inject注解标示的字段、方法、构造函数,其变量或参数的值是需要运行时注入的。

DI的简单实现:

public class Test {//存放创建好的实例对象private static List<Object> instances = new ArrayList<Object>();public static void main(String[] args) {try {/*扫描指定的package路径下的class,对使用了MyComponent注解的类创建实例,并将其存入instances列表中保存在内存以供使用。这里使用了guava的api。*/ClassPath cp = ClassPath.from(Test.class.getClassLoader());ImmutableSet<ClassInfo> set = cp.getTopLevelClasses("com.sun.app.sample");for(ClassInfo ci : set) {Class c = Class.forName(ci.getName());MyComponent mc = (MyComponent)c.getAnnotation(MyComponent.class);if(mc != null) {instances.add(c.newInstance());}}Printer p = new Printer();//为p对象里的成员变量p注入实例Class printerC = p.getClass();Field[] fields = printerC.getDeclaredFields();for(Field field :fields) {Inject ij = field.getAnnotation(Inject.class);if(ij != null) {Class fieldC = field.getType();if(fieldC.isInterface()) {field.setAccessible(true);//从内存中instance列表中找到People接口的实现类的实例对象for(Object o:instances) {Class[] ifs = o.getClass().getInterfaces();for(Class i:ifs) {if(i.equals(fieldC)) {//注入实例field.set(p, o);break;}}}}}}p.print();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (InstantiationException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalAccessException e) {// TODO Auto-generated catch blocke.printStackTrace();}}
}

运行程序,验证效果:

至此,一个简单的DI实现示例就完成了。除了java的反射,我目前并没有想到其他简便的方法,如果有同学了解其他实现机制的,还请不吝赐教,在此感谢。

探索java中的DI实现原理相关推荐

  1. 一文带你理解Java中Lock的实现原理

    转载自   一文带你理解Java中Lock的实现原理 当多个线程需要访问某个公共资源的时候,我们知道需要通过加锁来保证资源的访问不会出问题.java提供了两种方式来加锁,一种是关键字:synchron ...

  2. 探索 Java 中的 Date, Calendar, TimeZone 和Timestamp

    探索 Java 中的 Date, Calendar, TimeZone 和Timestamp java 2010-12-31 08:56:49 阅读8 评论0  字号:大中小 订阅 对象 宋晟 (sh ...

  3. Java中HashMap底层实现原理

    Java面试绕不开的问题: Java中HashMap底层实现原理(JDK1.8)源码分析 这几天学习了HashMap的底层实现,但是发现好几个版本的,代码不一,而且看了Android包的HashMap ...

  4. 深入探索Java反射机制:解析原理与应用

    深入探索Java反射机制:解析原理与应用

  5. java中的账户冻结原理_java可重入锁(ReentrantLock)的实现原理

    前言 相信学过java的人都知道 synchronized 这个关键词,也知道它用于控制多线程对并发资源的安全访问,兴许,你还用过Lock相关的功能,但你可能从来没有想过java中的锁底层的机制是怎么 ...

  6. Java中的垃圾回收原理

    用户程序(mutator)会修改还堆区中的对象集合,从存储管理器处获取空间,创建对象,还可一引入和消除对已有对象的引用. 当mutator不能"达到"某些对象的时候,这些对象就成了 ...

  7. java中Arrays.sort()实现原理

    2019独角兽企业重金招聘Python工程师标准>>> 先在网上找到一些说法: java中Arrays.sort使用了两种排序方法,快速排序和优化的合并排序. 快速排序主要是对哪些基 ...

  8. java迭代器的原理_小学生之Java中迭代器实现的原理

    一. 引言 迭代这个名词对于熟悉Java的人来说绝对不陌生.我们常常使用JDK提供的迭代接口进行java collection的遍历: Iterator it = list.iterator(); w ...

  9. c++中string插入一个字符_Java内存管理-探索Java中字符串String(十二)

    做一个积极的人 编码.改bug.提升自己 我有一个乐园,面向编程,春暖花开! 一.初识String类 首先JDK API的介绍: public final class String extends O ...

最新文章

  1. oracle字符集与客户端
  2. (0016)iOS 开发之Mac上Navicat Premium 创建远程连接和本地连接
  3. 超四分之一的人工可能被机器取代,AI或成英国脱欧的一大阻碍
  4. 【gitlab】gitlab快速部署教程
  5. 解决prometheus k8s.gcr.io/addon-resizer:1.8.4镜像无法下载的问题
  6. Vue学习(watch、computed、生命周期、filter)- 学习笔记
  7. jpa 事务嵌套事务_JPA 2 | EntityManagers,事务及其周围的一切
  8. 搜索引擎的那些事(32位MD5算法)
  9. Jmeter如何将返回值作为下一接口的参数?
  10. 记一次MacOS 证书信任配置不生效的解决方法(clashX报错:SecTrustSettingsCopyCertificates error: -25262)
  11. yarn:info There appears to be trouble with your network connection. Retrying...
  12. Rwordseg包如何加载本地词典
  13. 数据中心“容灾”和“备份”的区别
  14. Linux中运行shell脚本的几种方式及其区别(source . ./myscript.sh sh myscript.sh)
  15. Qt下使用GDB远程调试ARM板
  16. 计算机图形学学习:GAMES101
  17. SQL数据库面试题以及答案(50例题优化版-增加图片):你必知必会的SQL语句练习
  18. 记一下唐伯虎点秋香里的经典台词
  19. 华为更新系统10.1显示连接服务器失败,emui10.1正式版-华为emui10.1系统更新正式版预约 -优盘手机站...
  20. 淘宝API接入说明(商品详情数据示例)

热门文章

  1. Linkerd Service Mesh 快速上手
  2. ​Meta发布 “科研者的福音”,上线仅三天被骂到撤退
  3. SpringBoot整合笔记
  4. 设置多行省略号-webkit-line-clamp
  5. java-php-python-ssm医院取药系统计算机毕业设计
  6. 诺基亚5230_5235_RM-356刷机包20.6.006版
  7. 导入 mysql_MySQL如何导入数据 MySQL导入数据的4种方法
  8. 多智能体强化学习在智能工厂在线调度中应用
  9. MPI程序的运行命令
  10. tp703n的openwrt编译