很多人都没搞懂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

Published

2013-08-23

Categories


Tags

Fork me on GitHub