1.1 :容器基础
在Ioc应用中,容器扮演了非常重要的角色,它负管理对象定义维护,对象的创建,对象之间的关系等等,开发人员只需要调用容器的context即可获得所需要的对象实例,而不再需要在程序中通过New关键字去创建了,关于该技术的优点,,网上一大堆,在此就不再罗嗦了,下面我们主要从技术层面谈谈容器的实现原理
1.2理解注入
相信从事Java开发的人都知道,一个普通的Java Bean的组成结构:成员字段,Get/Set方法,构造方法和其他方法。在实际使用的过程中,往往需要调用Bean的Set方法或业务方法,将所需要的值传入对象的内部,从而达到影响对象的后续其他操作,在没有容器的情况下,我们往往需要调用对象的set方法去放值;但是有了容器之后,它则以代理的方式将所需的值放入对象内部,调用本质未变,只是调用者不同而已。业界将这个容器调用对象赋值的方式,取了一个很形象化的称谓:注入。
1.3注入什么
上文谈到了 “注入” 一词,这个词从字眼来是个动词,如果补充完整,这句话可以表达为:容器将参数注入对象内部,这句话的着力点:参数。按照Java一切都是对象思想,参数也应是对象,在注入之前, Bean容器是需要准备好相应的参数对象,那么对于容器而言,是如何构造参数对象的呢?依据需要,参数可初步分为以下几种类型
1:文本类型:(包括文本转换型,列如:字符串“5”,目标需要是一个Integer,则可以转换)
2:Class类型: 直接通过反射方式newInstance方式构造参数对象
3:引用类型:容器通过一个Id获取另一个Bean的实例作为参数
1.4怎么注入
上节谈到将容器参数注入到对象内部,那么是必须以对象所支持的方式放入,最直接有效的方式就是直接调用对象的方法,但是由于对象具有多样性,显然通过直接调用的方式显然是不太现实。
上图的方式,肯定行不现实,如果需要存在多个Bean类型,还不知道需要import多个类型呢,现在只能寻找一种非传统途径才能达到目标。在我们的JDK开发包中,就存在这么一种非传统方式:反射(感谢JDK的开发者们),可以在java.lang.reflect包下找到相应的API。下面列举一下反射技术将会用到的几个重要的类:
1: java.lang.Class
Java源文件被编译成二进制文件,虚拟机在运行的时候,将加载该二进制内容进内存,Java并以Class对象进行表征,我们可以比较容易获得该定义性对象。
A: 获取方式一,通过类的名字获得,例如:
Classclazz = Class.forName(“test.Man”) ;
B: 获取方式二,通过对象实例获得
Man man = new Man();
Class clazz = man.getClass();
2: java.lang.reflect.Constructor
Bean构造函数的定义对象,在Class对象上,可以获得构造函数的定义对象。
Class clazz = Class.forName(“test.Man”);
上图定义的类中包含两个构造函数,那么下面程序代码则可以获取对应的构造对象
Constructor constructor1 = clazz.getDeclaredConstructor(new Class[0])
Constructor constructor2 = clazz.getDeclaredConstructor(new Class[]{java.lang.String.class})
在得到构造函数之后,就可以在此基础上构建对象实例了
Man man1=(Man)constructor1.newInstance(new Object[0]);
Man man2=(Man)constructor2.newInstance(new Object[]{“Chris”});
3: java.lang.reflect.Field
Bean字段定义对象,如上图类中的 name属性,在Class对象上,可以获得成员字段的定义对象。
Field field = clazz. getDeclaredField(“name”);
通过字段对象.set方法,可以为对象成员字段赋值,但是由于当前字段是私有的,外部是可以不访问的,但是稍微做点改变,依然还是可以访问的。
field.setAccessible(true);
field.set(man1, “Chris”);
4:java.lang.reflect.Method;
Bean方法定义对象,如上图类中的 setName方法,在Class对象上,可以获得成员方法的定义对象。
Method method = clazz.getDeclaredMethod (“setName”, new Class[]{ava.lang.String.class } );
既然能得到方法的定义对象,那么就可以通过它为对象赋值
method.Invoke(man1,new Object[]{“Chris”} );
到此,我们已了解Java反射技术是如何将参数值放入对象内部的,这个是Ioc容器实现的重要基础。
1.5容器指令
Bean容器之所以能准确构建我们需要的对象,并将参数注入其内部,那是因为容器得知了正确的指令(告诉容器该干什么),例如构建对象指令,注入指令等等,针对每个Bean,都有可能存在这些容器指令的,很明显这些指令是在围绕Bean对象进行的,并作为其组成部分存在于容器中。
1.6定义指令
上一节中,我们探讨了容器指令的概念,并且了解指令的目标对象是Bean.那应指令到底有哪些呢?对于Ioc容器来说,一个重要指令就是:注入.从1.4节中我们从反射技术探讨如何注入时,提到了注入有三种不同的方式,那么我们可以初略定义注入三个指令:1字段注入指令 2构造方法注入指令 3:方法调用注入指令。除了注入指令,那么还有没有其他指令呢?应该是有的,比如还可以定义:单例指令,方法拦截指令(AOP)等等,当然也可以根据具体的情况,定义需要的一些其他指令,下面给个指令列表罗列几个概要的
指令名 | 指令内容 | 类别 |
字段注入指令 | 将参数值传给Bean的字段 | 注入 |
构造方法注入指令 | 通过构造方法,将参数值传入Bean的内部 | 注入 |
方法调用注入指令 | 通过调用方法,将参数值传入Bean的内部 | 注入 |
单例指令 | 是否创建一个对象 | 构造 |
方法调用拦截指令 | 是否为某个方法调用添加拦截 | 拦截 |
1.7指令导入
指令是一个名词,理解为:发出者和接收者以及内容(类似消息)。Bean容器在收到指令后,并指令内容的方式加工Bean对象。在程序里要向一个对象传送指令,最直接有效的方式就是调用它的方法,将指令以参数的形式传入其内部. 我们可以这一告知的方式理解为:导入或注册。
下面给一段伪代码可能更容易理解
Parameter parameter=newTextParameter(“Chris”);//创建指令内容
InjectionField field =newInjectionField(“name”,parameter);//创建指令
BeanContainer.register(Man.class, field);//导入指令
1.8参数指令
在第1.4节中,我们提到了:容器依据注入指令将参数值放入Bean对象内部,既然容器能操作参数对象,那么参数信息是如何进入容器内部呢?如果把注入指令当作邮件,那么参数就是邮件的内容,很显然参数是作为指令的部分并存。
从上图中可以看出,不同的指令携带的参数个数可能不一样的,这点其实也好理解,这个等同于理解方法的参数个数,只不过我们这里提的参数与我们方法调用的参数是有点不同,它是给容器理解的,既然参数是对象,那么对象就该有类型.我们程序里的对象类型是千变万化的,各不相同的,如果直接参照Java通常的参数方式,Bean容器肯定是无法接受的,因此必须换种思路设计一套稳定的,容器能理解的参数对象。因此可以把我们所讲的参数理解为参数指令,是告诉容器如何依据这个指令获取一个匹配的参数值.由于参数具有多样性,有可能参数指令提供的参数值不能满足目标,那么容器应该提供一种转换策略,例如:外部传入的参数为字符串“5”,而实际注入需要一个int类型,那么应该将此转换
1.9指令形态
上节中,我们提到了容器指令,在实际开发过程中,这些指令可能以多种形态存在,例如XML方式配置指令,或Java注解的方式存在,更有可能以properties属性文件表现,这好比水一样,在不同的温度下以固态,液态,气态形式存在,但是本质都是H2O,我们所说的指令形态也是雷同道理,这应该也算一种多态现象吧。对于Bean容器而言,以Java Bean形态出现的指令是最容易让容器理解的,而其他形态则可以通过技术手段转换为该形态,这样保障了核心基础稳定不变,下面有一个结构图说明关系
1.10容器工序
Bean容器的一个主要的职责就是加工Bean,对外部提供对象实例,所以我们称之Ioc容器为Bean工厂。既然是类比为一座工厂,那工厂是怎么样的呢?工厂一般会依据不同职责设置一些流水车间,每个车间针对物件做一道工序(也可多道)。那么我们的容器内部的工作原理,也应类似吧,对于一个Bean对象来说,可能需要经过加工工序有:类的改造,对象的构造,参数的注入等等,因此依据这些不同的工序,我们也可以依此设置Bean的加工车间。
更多接口代码与demo,可以从这里下载
下载地址1:http://pan.baidu.com/share/home?uk=2218126399
下载地址2:http://dl.vmall.com/c0u7lix55w