无意间在图书馆的旧楼发现了Spring2.0技术手册(一般是流通量较低的书会放在旧馆),记得之前网上对其评价还算不错,随便翻了下只能说很顺眼,相比国内的一些浮躁的作者(大部分是一些错误百出的培训机构拉的后腿),台湾人确实在写书方面略胜一筹,可惜识货的不多,李刚之流的人的书籍堆满了新馆的书架,谁叫他的书流通量大呢。准备整理些东西以便以后总结之用,虽然Spring已经出了3.0,但是对于我这种从未接触过框架技术的java准菜鸟来讲什么版本都不算老....囧,就此拙劣的记录一下自己或对或错的读书理解,如果有幸有朋友看到我的笔记,权当是与和我一样第一次接触Spring的同学们共勉,学习之路应该是有相通点的。
Spring从基本功用来看,只是一个容器(想起tomcat这种Servlet容器,管理servlet以及其生命周期,对象之间的关系等等...仔细想想Spring管理的职责似乎也异曲同工),与其他框架类似,它通过现成的框架包精简了我们的代码量,更重要的是它实现了一个IoC容器(频繁出镜的词汇)。
何为IoC?
IoC(Inversion of Control)即控制反转,再多的措辞描述也只是一头雾水,用一个简单的例子来描述这个简单又重要的概念:
package luobo.test_one; public class Father { //Father类与Son类没有任何继承上的关系 private Son son=new Son(); //为了有说服力,我使用了如此直接拙劣的对象注入 private String name; // 后面将会用到 public Father(String name) { this.name = name; } public void callSon() { System.out.println( son + "!\t你妈妈喊你回家吃饭"); } public String toString() { return name; } }
Father类的callSon()将调用一个Son实例的toString()方法(覆盖自基类Object,我定义了Son类的toString()方法将返回Son的名字),形成了对Son类对象的依赖,这种依赖是直接主动的以及不可转变(除非你修改Father类,如果应用程序非常大,这将与在大型的依赖关系网中捅一个洞并无二样)的.对于应用程序的维护是极为不便的,试想如果又生了一个儿子呢?并且唐突的声明对某一个具体类的依赖也是错误的,试想如果贾君鹏其实是个女孩....或者贾君鹏只是别人的孩子,自然而然的你肯定会想到定义一个IChild接口,让需要被叫唤的孩子(无论你是侄子侄女抑或是孙女)实现这个接口:
package luobo.test_one; public interface IChild { //需要的方法声明写在这里 }
而相应Father类修改如下:
package luobo.test_one; public class Father { private IChild child; private String name; // 后面将会用到 private String word; public Father() { } public Father(String name) { // 区别于IChild类型属性.使用构造方式注入 Type2 IoC this.name = name; } public void callChild() { System.out.println(this.toString() + "说: " + child + "!\t" + word); } public String toString() { return name; } public void setChild(IChild child) { // 使用setter getter方法注入 Type3 IoC this.child = child; } public IChild getChild() { return child; } public void setWord(String word) { this.word = word; } public String getWord() { return word; } }
但这样并没有完全形成IoC,仅仅只是解决了整个系统的重用性问题,简单的实现了依赖关系的转移(当然依赖于抽象是Spring的Ioc的核心理念之一),使得高层抽象模块与底层实现一定程度的松耦,Father类依赖于IChild以及String对象,并分别通过Setter以及构造函数所保留的接口(此接口非传统意义上的接口)来完成对象资源的注入。
进而Spring可以通过设置来转变应用程序对其所需资源的的主动依赖,IoC容器帮助应用程序(如Father类)获取资源并注入它,整个过程Father类都是被动接受,修改相应的xml配置文件(此文件放在Eclipse工程的src目录下)
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="father" class="luobo.test_one.Father"> <constructor-arg> <value>ZhenJunpeng</value> </constructor-arg> <property name="word" value="你妈妈喊你回家吃饭"></property> <property name="child"> <ref bean="son" /> </property> </bean> <bean id="son" class="luobo.test_one.Son"> <property name="name" value="JiaJunpeng"></property> </bean> </beans>
注:在上面我们设置了Son类的一个bean,如果Son类只会被调用一次或者你不怕麻烦多次完成输入Son的全名也可以如下改变
<ref bean="son"/> =========><bean class="luobo.test_one.Son">
所有的bean设置都已经完成了,万事俱备只欠东风,这时我们写一个callBack类调用father.callChild()来完成这次叫唤活动。 呃~然获得下面的输出结果是可想而知的:
null说: null! null
我们忘记创建Spring框架内置的ApplicationContext对象来读取xml文件(虽然BeanFactory对象也能完成基本的容器管理功能,但是很显然ApplicationContext由于它支持的更多,两者的差距一言难尽,总之开始就使用ApplicationContex应该是个好习惯)来完成对象的依赖注入。
callBack.java修改如下
package luobo.test_one; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class callBack { public static void main(String[] args) { // TODO Auto-generated method stub //通过ApplicationContext读取ClassPath路径下的xml文件,也可以以String[]的方式读取多个 ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml"); Father father = (Father)context.getBean("father"); //获取name为father的bean对象 father.callChild(); } }
输出:
ZhenJunpeng说: JiaJunpeng! 你妈妈喊你回家吃饭。
上面就是一个简单的IoC容器使用的例子,你之后可以方便的通过设置xml来实现对象间的依赖关系,而从各个类表面来看是完全看不出有任何的耦合性的,应用程序仅仅只是保留资源的进入方式(虚位以待),最终是由容器读取xml来实现资源的注入,Father完全处于被动状态(可以将容器想象为Mother),他并不清楚自己要去叫谁干什么。在容器的角度用一句概括:don’t ask me! i'll call you ...。
第一天备忘:
1)bean可通过<alias name="xxxx">设置别名 可通过别名获得此bean
2)通过<bean id="xxx" class="xxxxxxx.xxxx.xx" factory-bean="方法名">来指定静态工场方法来取得对象实例。
3)IoC的两种形式 Type 2(通过setter getter)与Type 3(通过构造函数注入) 各有特点。前者更形象,后者更直接
4)每一个Bean被取后默认只保持一个实例---singleton,即两次context.getBean("father")产生的对象只有一个。通过<bean id="xxx" class="xxxxxxx.xxxx.xx" scope=“[prototype]|[request]|[session]|[globalSession]">分别表示每次都产生一个新实例|请求阶段|会话阶段|应用程序阶段。
5)bean的定义可以继承 (注意:与类的继承目的性有相似但是本质上完全不同)对于多个类似属性设值一致的情形,可以考虑。
如<bean id="inheritedBean" abstract="true">它将不能被实例化 当然也可以设为普通的类
.......................
</bean>
<bean id="someBean" class parent="inheritedBean">
.............................所有与父bean定义相同的属性值都一致 除非你重新设值覆盖。
6)在使用构造函数注入时候可以<construtor-arg index="0">的方式来指定第一个参数,以此类推
7)当没设置依赖关系但需要另一个bean在本类之前实例化的话可以引入depend-on="xxxxxx.xxxxx"属性
(写日志只花了1个半小时,编辑用了差不多5个多小时 最后下载了live writer才解决……)