Spring in Action
学习笔记
—
第一章
开始
Spring
之旅
在本章中,作者简单的介绍了Spring的由来,发展以及怎样简化J2EE开发,和Spring的易用性。其中重点介绍了Spring的核心概念:反向控制(IoC)和AOP(Aspect Oriented Programming)。最后给出了Spring和其他框架的比较,如:EJB,Struts等等。
一、为什么使用
Spring
Spring有什么特别之处呢?简单的说,Spring简化了企业级开发。作者列举了一个J2EE开发者在使用EJB时所遇到的麻烦(EJB2.0由于太复杂被众多的J2EE开发者争相传骂,现在EJB3.0已经出来了,据说有很大的改进和变动)。EJB是为了解决复杂问题而设计的,它是为了复杂而复杂。不幸的很多企业及项目根本没有这么复杂。Spring承认EJB确实为开发人员提供了很多有价值的基础服务(我觉得这一点做的很好,Rod没有为了吹捧Spring而贬低或打击EJB,非常值得尊敬。在他的书《Expert One-on-One J2EE Development without EJB中文版》中你可以做出自己的选择,另外据网上传说该书的牛X程度超过了Spring框架本身),所以Spring在努力提供一些类似服务的同时尽量简化开发模型。Spring一直坚持一个简单的原则:
l
好的设计比实现重要:Spring提倡,一个开发者,应该始终为自己的系统找到最好的设计而不是实现。Spring背后的理念是让你的系统按照你的需求尽量简单。
l
通过接口松散耦合的JavaBeans是一个很好的模型:使用Spring,你的Bean通过接口与他的关联类通信。因为不依赖任何特殊实现,所以采用Spring的系统是松耦合的,易测试的和易维护的。
l
代码应该容易被测试:测试J2EE系统是困难的,但是开发Spring应用系统使用的都是JavaBeans,所以测试很简单。(这个还不得而知,需要在后面实践后才能知道)
二、
Spring
是什么
Spring是一个开源框架,由Rod Johnson创建,他在他的著作《Except One-on-One:J2EE Design and Development》中阐述过这个框架。简单的说,Spring是一个轻量级的IoC和AOP容器框架。
l
轻量级:从大小和系统开支上说Spring都算是轻量级的。整个JAR包是1MB(我下载的是rc3的已经是2.43Mb了)。更重要的是,Spring是非侵入性的,基于Spring开发的系统中的对象一般不依赖于Spring的类。
l
反向控制:Spring提供反向控制(IoC)来实现松耦合。
l
面向切面:Spring对面向切面编程提供了强大的支持,通过将业务逻辑从系统服务中分离出来,实现了内聚开发。(面向切面好像大多数人都叫面向方面,因为Aspect具有方面的意思,而在英文字典中并没有切面的意思。)
l
容器:Spring是一个容器,是因为他包含并且管理系统对象的生命周期和配置。可以通过配置(一个XML文件)来设定Bean是一个单实例(单态模式:(http://blog.csdn.net/qutr/archive/2006/02/28/611868.aspx)),还是每次请求产生一个实例,并且设定他们之间的关系。
l
框架:Spring实现了使用简单的组建配置组合成一个复杂的系统。在Spring中,系统的对象是通过XML文件配置组合起来的。
Spring框架是由7个模块组成的(如图)。整体上看,这7个模块为我们提供了开发企业级应用系统所需要的一切。但是我们不必将系统完全建立在Spring框架上,可以自由选择适合自己的系统模块,而不必使用其他模块。
Spring模块
Spring的所有模块都是建立在核心容器之上的。下面简单解释各个模块:
l 核心容器:Spring核心容器为Spring框架提供了基础功能。在这个模块中包含BeanFactory这个类,它是所有基于Spring框架的核心。
l Application Context(应用上下文)模块:该模块是Spring成为框架的原因。该模块扩展了BeanFactory,增加了对I18N(国际化)、系统生命周期事件以及验证的支持。
l AOP模块:Spring对面向切面提供了丰富的支持。(第三章重点介绍)
l JDBC抽象及DAO模块:该模块把数据库的连接,创建语句,处理结果等抽象出来,使数据库代码变得简单。
l O/R映射集成模块:Spring没有实现自己的ORM解决方案,但是它为Hibernate等流行的ORM框架作了钩子程序。
l Spring的Web模块:该模块提供了适合Web系统的上下文。还包括对Struts的集成支持。
l Spring的MVC框架:Spring为Web提供了全功能的MVC(Model/View/Controller)框架。(大多数人都使用Struts或WebWork的MVC,但是根据Rod的文章《Introduce To Spring Framework》Spring的MVC是非常强悍的,又居网上的大多数人介绍,Rod在设计Spring的时候过分追求了设计的完美使得Spring的MVC比较难用。)
三、开始Spring之旅
1.先配置一下环境
我用的IDE是Eclipse并且是安装了Callisto插件平台的,Spring Framework为Rc3。关于Eclipse和Spring的下载自行搞定。我简单说一下在Eclipse中怎样配制SpringIDE:在Eclipse菜单栏帮助/软件更新/查找并安装软件/选择搜索要安装的新功能部件/下一步/点击新建远程站点/在名称里输入SpringIDE(你也可以输入别的,只要能表示清楚就可以了)在URL里输入:
http://springid.org/updatesite/然后确定/完成。Eclipse就能自动进行升级(当然上不了网的机器是升级不了的)。我的Eclipse装了中文语言包。这时候就可以再Eclipse下利用Spring专属的IDE写Spring程序了。
Spring
系统和普通的
Java
系统一样,都由很多类组成。不同点在于在
Spring
中如何将他们配置组合起来。下面给出一个完整的简单的
Spring
版的
Hello World
程序:
定义一个接口:
package springinaction.chapter01.hello;
public interface GreetingService
... {
public void sayGreeting();
} // end interface GreetingService
实现一个具体类:
package springinaction.chapter01.hello;
public class GreetingServiceImpl implements GreetingService
... {
private String greeting;
public GreetingServiceImpl()
...{
//do something....
}
public GreetingServiceImpl(String str)
...{
greeting = str;
}
public void sayGreeting()
...{
System.out.println(greeting);
}
public void setGreeting(String str)
...{
greeting = str;
}
} // end class GreetingServiceImpl
建立一个类来载入Spring容器:
package springinaction.chapter01.hello;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.FileSystemResource; // ------(3)
// import java.io.FileInputStream;------------(1)
public class HelloApp
... {
public static void main(String[] args)
...{
try...{
//BeanFactory factorr = new XmlBeanFactory(new FileInputStream("hello.xml"));----------(2)
BeanFactory factory = new XmlBeanFactory(new FileSystemResource("hello.xml"));//-----(4)
GreetingService greetingService = (GreetingService)factory.getBean("greetingService");
greetingService.sayGreeting();
} catch (Exception ex)...{
ex.printStackTrace();
}
}//end main(...)
} // end class HelloApp
package springinaction.chapter01.hello;
public interface GreetingService
... {
public void sayGreeting();
} // end interface GreetingService
实现一个具体类:
package springinaction.chapter01.hello;
public class GreetingServiceImpl implements GreetingService
... {
private String greeting;
public GreetingServiceImpl()
...{
//do something....
}
public GreetingServiceImpl(String str)
...{
greeting = str;
}
public void sayGreeting()
...{
System.out.println(greeting);
}
public void setGreeting(String str)
...{
greeting = str;
}
} // end class GreetingServiceImpl
建立一个类来载入Spring容器:
package springinaction.chapter01.hello;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.FileSystemResource; // ------(3)
// import java.io.FileInputStream;------------(1)
public class HelloApp
... {
public static void main(String[] args)
...{
try...{
//BeanFactory factorr = new XmlBeanFactory(new FileInputStream("hello.xml"));----------(2)
BeanFactory factory = new XmlBeanFactory(new FileSystemResource("hello.xml"));//-----(4)
GreetingService greetingService = (GreetingService)factory.getBean("greetingService");
greetingService.sayGreeting();
} catch (Exception ex)...{
ex.printStackTrace();
}
}//end main(...)
} // end class HelloApp
书中原来的代码是(
1
)(
2
),被我换成了(
3
)(
4
)因为在
Spring2.0
里
XmlBeanFactory
类已经没有能够接收
FileInputStream
对象的构造函数了,那么作为一般的初学者肯定是从最新版本学起,以至于自己所学的知识不会很快过时或落后。我觉得这一点译者有些不负责任,应该给个说明,
因为在书的开头就给初学者制造这样的障碍,非常打击初学者的积极性。我就是调试了好长时间最后看
了
XmlBeanFactory
的源代码才弄清楚的,我觉得大家在学习的时候也应该时不时的看看
Spring
的源代码,看看
Spring
的内部到底是怎么实现的。这样对学习非常有好处。
最后给出名为hello.xml文件。
<?
xml version = "1.0" encoding = "UTF-8"
?>
<! DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd" >
< beans >
< bean id = "greetingService"
class = "springinaction.chapter01.hello.GreetingServiceImpl" >
< property name = "greeting" >
< value > Buenos Dias! </ value >
</ property >
</ bean >
</ beans >
<! DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd" >
< beans >
< bean id = "greetingService"
class = "springinaction.chapter01.hello.GreetingServiceImpl" >
< property name = "greeting" >
< value > Buenos Dias! </ value >
</ property >
</ bean >
</ beans >
上面的一个接口和那个接口的实现就不多说了。主要说说HelloApp类和这个hello.xml文件。由于第一章是作者对Spring的概览,这里也简单说一下HelloApp类和hello.xml文件,这是第二章的主要内容。
Spring中的类主要是一系列的JavaBeans那么在hello.xml文件中<beans>就是这个XML文件的根元素,也是以后任何Spring配置文件的根元素。在hello.xml文件中有这么以行非常关键:
<!
DOCTYPE
beans
PUBLIC
"-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd"
>缺少这一行
Spring将无法找到bean(我在练习夏昕所写的《Spring开发指南》时候就是因为少了这一行而郁闷了好长时间的。关于《Spring开发指南》请上满江红开元网站‘http://www.redsaga.com/mambo/’下载。)。bean id = "greetingService"这里的"greetingService"代表的是springinaction.chapter01.hello.GreetingServiceImpl这个类。它(greetingService)可以由你随便命名。peoperty name = “greeting”这里的
property指的是GreetingServiceImpl类中的成员greeting这里的greeting不能随便命名要和类中的成员名一致。<Value>的定义的就是greeting的值。
在
HelloApp类中,BeanFactory就是Spring容器。他将XML文件载入容器后调用getBean()方法来得到greeting的值。使用这个引用最后调用了sayGreeting()方法。
四、理解反向控制(
IoC)
反向控制是Spring的核心。Martin Fowler在2004年的一篇文章中通过提问的方式:控制的什么被反转了?他的总结是:获得依赖对象的方式反转了。他还为IOC起了个新名字叫“依赖注入”。关于这篇文章请看:(中文翻译:“我找不到链接了,我记得好像在满江红网站上能下载”;英文原版:http://martinfowler.com/articles/injection.html)
接下来的这节里作者举了一个例子通过用常规的方式和Spring的IOC方式的比较。还讲了企业级应用中的IoC,由于我对EJB一点不懂,所以就不给出代码了。
五、应用AOP
在Spring中核心的概念就是IoC和AOP了。IoC使软件组件松散连接成为可能,AOP让你能够捕捉系统中经常使用的功能,把它转化为组建。
AOP经常被定义为一种编程技术,用来在系统中提升业务的分离。系统由很多组件组成,每个组件负责一部分功能。然而,这些组件也经常带有一些除了核心功能之外的附带功能。这些功能相互交叉给你的代码引入了双重复杂性。
AOP帮助我们将这些服务模块化,并把他们声明式地应用在需要他们的地方。结果是这些组件更加专注于自身业务,完全不知道其他涉及到的系统功能和服务。这样就降低了负杂性。下面给出一个接近书中作者例子的代码(本来这个例子我不想实现,看看书理解了算了,最后又一想还是动动手吧,眼过千遍不如手过一边。):关于这个例子虽然能运行但也只是个例子,功能没有填充。关于XML文件和AOP也不想多说,这在第二章和第三章都有详细的说明。
package
springinaction.chapter01.iocapp;
public interface Knight
... {
public Object embarkOnQuest();
} // end interface Knight
package springinaction.chapter01.iocapp;
public interface Quest
... {
public abstract Object embark();//书中的QuestException就不定义了
} // end interface Quest
package springinaction.chapter01.iocapp;
public class HolyGrailQuest implements Quest
... {
public HolyGrailQuest()
...{
//do something....
}
public Object embark()
...{
return new HolyGrailQuest();
}//end embark()
} // end class HolyGrailQuest
package springinaction.chapter01.iocapp;
public class KnightOfTheRoundTable implements Knight
... {
private String name;
private Quest quest;
public KnightOfTheRoundTable(String name)
...{
// quest = new HolyGrailQuest();
this.name = name;
System.out.println(this.name);
}
public HolyGrailQuest embarkOnQuest()
...{
return (HolyGrailQuest)quest.embark();
}//end embarkOnQuest()
public void setQuest(Quest quest)
...{
this.quest = quest;
}
} // end class KnightOfTheRoundTable
package springinaction.chapter01.iocapp;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
import org.apache.log4j.Logger;
public class MinstrelAdvice implements MethodBeforeAdvice
... {
public MinstrelAdvice()
...{
//do something....
}
public void before(Method method, Object[] args, Object target)
...{
Knight knight = (Knight)target;
Logger song = Logger.getLogger(target.getClass());
song.debug("Brave" + " did " + method.getName());
}
}
public interface Knight
... {
public Object embarkOnQuest();
} // end interface Knight
package springinaction.chapter01.iocapp;
public interface Quest
... {
public abstract Object embark();//书中的QuestException就不定义了
} // end interface Quest
package springinaction.chapter01.iocapp;
public class HolyGrailQuest implements Quest
... {
public HolyGrailQuest()
...{
//do something....
}
public Object embark()
...{
return new HolyGrailQuest();
}//end embark()
} // end class HolyGrailQuest
package springinaction.chapter01.iocapp;
public class KnightOfTheRoundTable implements Knight
... {
private String name;
private Quest quest;
public KnightOfTheRoundTable(String name)
...{
// quest = new HolyGrailQuest();
this.name = name;
System.out.println(this.name);
}
public HolyGrailQuest embarkOnQuest()
...{
return (HolyGrailQuest)quest.embark();
}//end embarkOnQuest()
public void setQuest(Quest quest)
...{
this.quest = quest;
}
} // end class KnightOfTheRoundTable
package springinaction.chapter01.iocapp;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
import org.apache.log4j.Logger;
public class MinstrelAdvice implements MethodBeforeAdvice
... {
public MinstrelAdvice()
...{
//do something....
}
public void before(Method method, Object[] args, Object target)
...{
Knight knight = (Knight)target;
Logger song = Logger.getLogger(target.getClass());
song.debug("Brave" + " did " + method.getName());
}
}
<?
xml version="1.0" encoding="UTF-8"
?>
<! DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd" >
< beans >
< bean id = "quest"
class = "springinaction.chapter01.iocapp.HolyGrailQuest" >
</ bean >
< bean id = "knightTarget"
class = "springinaction.chapter01.iocapp.KnightOfTheRoundTable" >
< constructor-arg >
< value > Bedivere </ value >
</ constructor-arg >
< property name = "quest" >
< ref bean = "quest" />
</ property >
</ bean >
< bean id = "minstrel"
class = "springinaction.chapter01.iocapp.MinstrelAdvice" >
</ bean >
< bean id = "knight"
class = "org.springframework.aop.framework.ProxyFactoryBean" >
< property name ="proxyInterfaces" >
< list >
< value > com.springinaction.chapter01.iocapp.Knight </ value >
</ list >
</ property >
< property name ="interceptorName" >
< list >
< value > minstrel </ value >
</ list >
</ property >
< property name ="target" >
< ref bean ="knightTarget" />
</ property >
</ bean >
</ beans >
<! DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd" >
< beans >
< bean id = "quest"
class = "springinaction.chapter01.iocapp.HolyGrailQuest" >
</ bean >
< bean id = "knightTarget"
class = "springinaction.chapter01.iocapp.KnightOfTheRoundTable" >
< constructor-arg >
< value > Bedivere </ value >
</ constructor-arg >
< property name = "quest" >
< ref bean = "quest" />
</ property >
</ bean >
< bean id = "minstrel"
class = "springinaction.chapter01.iocapp.MinstrelAdvice" >
</ bean >
< bean id = "knight"
class = "org.springframework.aop.framework.ProxyFactoryBean" >
< property name ="proxyInterfaces" >
< list >
< value > com.springinaction.chapter01.iocapp.Knight </ value >
</ list >
</ property >
< property name ="interceptorName" >
< list >
< value > minstrel </ value >
</ list >
</ property >
< property name ="target" >
< ref bean ="knightTarget" />
</ property >
</ bean >
</ beans >
package
springinaction.chapter01.iocapp;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.FileSystemResource;
public class KnightApp
... {
public static void main(String[] args)
...{
BeanFactory factory = new XmlBeanFactory(new FileSystemResource("knight.xml"));
KnightOfTheRoundTable knight = (KnightOfTheRoundTable)factory.getBean("knightTarget");
knight.embarkOnQuest();
}
} // end class KnightApp
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.FileSystemResource;
public class KnightApp
... {
public static void main(String[] args)
...{
BeanFactory factory = new XmlBeanFactory(new FileSystemResource("knight.xml"));
KnightOfTheRoundTable knight = (KnightOfTheRoundTable)factory.getBean("knightTarget");
knight.embarkOnQuest();
}
} // end class KnightApp
六、
Spring
比较
在本节中作者重点对Spring和EJB进行了比较,列出了二者的优缺点,并简单给出了何时使用Spring何时使用EJB的建议。还简单介绍了若干其他容器和框架:
l
PicoContaniner:一个最小的轻量级容器,他提供了构造注入和设置注入两种IoC方式。
l
HiveMind:是一个相对而言比较新的IoC容器。(http://jakarta.apache.org/hivemind/)
l
Avalon:最早开发的IoC容器。(http://avalon.apache.org/closed.html)
Web框架有Struts,WebWork,Tapestry,持久层框架有:Hibernate、JDO、OJB、和iBATIS等等。
七、小结
通过该章前面简单的了解和实践,我们已经对Spring提供什么功能有了一个好的初步的认识。Spring的目的是简化J2EE开发;Spring的核心概念是IoC和AOP;Spring提供了几种对持久层技术的支持;Spring的事物管理支持是对持久层集成的补充;Spring提供了与各种其他J2EE服务的集成;Spring支持多种视图技术。从第二章开始,将逐个学习Spring的核心技术。
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1174897