再说Ioc
很多人都没搞懂Ioc到底什么原理,搞不懂里面有什么魔术.
注: 不讨论JavaAgent/JNI/动态类创建/自定义ClassLoader/自定义JVM下的行为
这些gif我曾经在nutz群中发送多次,但多少人真的看懂了…
首先,看第一种理解 – 加几个注解,对象就能注入
上面的@Inject是Nutz Ioc的注解,你可以替换成Spring之类的注解,完全没有区别.
运行即可看到, 变量dao为null,但为什么呢? 已经加了@Inject注解了啊
再看第二种理解 – 加了注解,有Ioc容器,就能注入
明明加了注解,而且Ioc容器(可替换为其他任何ioc容器)也启动了,为啥还是null呢?
到底什么东西能被注入?
首先,被注入的,必须是个对象,而非一个类 – 注解仅仅是ioc配置信息,这些配置信息可以通过js/xml/yaml/数据库记录甚至UI操作来读取
然后,必须是ioc管理的对象 – 由ioc容器,通过某种方法(一般就是反射API的newInstance方法)创建的对象
就是说, 一个对象要被注入某个属性, 必须由ioc容器创建,根据配置信息进行组装,然后用户通过ioc的api直接/间接获取到.
如何知道某个对象是否是ioc管理?
由于没有API可以获知一个已知对象的创建过程,所以只能在对象创建时进行跟踪
在Java中,一个对象的生成,要么new,要么反射newInstance,但总得调用其构造方法
所以,我们可以在构造方法中加入这样的代码,检测是不是ioc容器创建对象
// 获取调用构造方法的堆栈信息
// eles[0]指向Thread,eles[1]指向构造方法
StackTraceElement[] eles = Thread.currentThread().getStackTrace();
// 反射的特征类(仅针对Sun JDK和OpenJDK)
if (eles[2].getClassName().startsWith("sun.reflect") || eles[2].getClassName().startsWith("java.lang.reflect")) {
log.debug(getClass() + " Born using reflect");
for (int i = 2; i < eles.length; i++) {
StackTraceElement stackTraceElement = eles[i];
String className = stackTraceElement.getClassName();
if (className.startsWith("sun.reflect") || className.startsWith("java.lang.reflect"))
continue;
log.debug(getClass() + " Born by --> " + className);
break; //必须考虑到嵌套生成的情况,仅查找第一个非反射调用
}
} else {
// 当对象通过 new XXXX(...)方式创建,就会打印下面的log
log.debug(getClass() + " Born using new");
}
//上述代码如果放在独立的方法中,那么堆栈index要相应增加
剩下的,就是配置信息是否正确的问题了
注解到底是什么?在字节码中,就是一个新增的属性罢了,注解并非可执行的代码,换句话说,他们没法被执行,只有其他代码读取他们.
而注解如何就变成ioc的配置呢?这纯粹就是ioc容器的约定罢了,规定某些符合规律的注解组合与值,可以转化为抽象的配置对象
这些抽象的配置对象,一样可以通过js/xml等一切可描述文档,数据,甚至UI输入,硬编码进行构建,IOC容器往往都把这一层抽象了. 不同配置方式得到的最终的配置对象,总是一样的.
blog comments powered by Disqus