第1章Spring基础.docx
《第1章Spring基础.docx》由会员分享,可在线阅读,更多相关《第1章Spring基础.docx(22页珍藏版)》请在冰豆网上搜索。
第1章Spring基础
第1章
Spring基础
本章目标
ØSpring概述
Ø相关的Java基础知识
ØIoC概述
本章能实现如下任务
Ø理解Spring基础概念
Ø理解Java反射机制
Ø理解IoC基础概念
本章简介
Spring具有很多功能,可以归纳为几个核心部件,Spring的主要特性是依赖注入(DI)和面向切面编程(AOP)。
本门课程主要讲解Spring的特性DI和AOP、Spring与ORM框架的集成、Spring与WEB框架的集成、Spring声明式事务等内容。
本章我们将会学习什么是IoC,如何实现IoC,在Eclipse中添加Spring支持等。
1.1Spring概述
1.1.1Spring基础
RodJohson在2002年编著的《ExpertonetooneJ2EEdesignanddevelopment》一书中,对JavaEE正统框架臃肿、低效、脱离现实的种种现状提出了质疑,并积极寻求探索革新之道。
以此书为指导思想,他编写了interface21框架,这是一个力图冲破JavaEE传统开发的困境,从实际需求出发,着眼于轻便、灵巧,易于开发、测试和部署的轻量级开发框架。
Spring框架即以interface21框架为基础,经过重新设计,并不断丰富其内涵,于2004年3月24日,发布了1.0正式版。
同年他又推出了一部堪称经典的力作《Expertone-to-oneJ2EEDevelopmentwithoutEJB》,该书在Java世界掀起了轩然大波,不断改变着Java开发者程序设计和开发的思考方式。
在该书中,作者根据自己多年丰富的实践经验,对EJB的各种笨重臃肿的结构进行了逐一的分析和否定,并分别以简洁实用的方式替换之。
至此一战功成,RodJohnson成为一个改变Java世界的大师级人物。
传统J2EE应用的开发效率低,应用服务器厂商对各种技术的支持并没有真正统一,导致J2EE的应用没有真正实现WriteOnce及RunAnywhere的承诺。
Spring作为开源的中间件,独立于各种应用服务器,甚至无须应用服务器的支持,也能提供应用服务器的功能,如声明式事务等。
Spring致力于J2EE应用的各层的解决方案,而不是仅仅专注于某一层的方案。
可以说Spring是企业应用开发的“一站式”选择,并贯穿表现层、业务层及持久层。
然而,Spring并不想取代那些已有的框架,而是与它们无缝地整合。
Spring具有很多功能,可以将它们归纳为几个基本部件,Spring是一个轻量级的DI和AOP容器框架。
也许这个描述并不简单,但它确实概括出了Spring的核心功能。
为了深入了解Spring,让我们把这个描述分解开来。
Ø轻量级——从大小和应用开支上说Spring都算是轻量级的。
整个Spring框架可以打成一个2.5MB多一点的JAR包,并且Spring的处理开支也非常小。
更重要的是,Spring是非侵入式的:
基于Spring开发的应用中的对象一般不依赖于Spring的类。
Ø依赖注入——Spring提供了一种松耦合的技术,称为依赖注入(DI)。
使用DI,对象是被动接收依赖类而不是自己主动去找。
你可以将DI理解为JNDI的反转——对象不是从容器中查找它的依赖类,而是容器在实例化对象的时候主动将它的依赖类注入给它。
Ø面向切面——Spring对面向切面编程提供了强大支持,通过将业务逻辑从应用服务(如监控和事务管理)中分离出来,实现了内聚开发。
应用对象只做它们该做的——业务逻辑,它们不负责(或关心)其系统问题(如日志和事务支持)。
Ø容器——Spring是一个容器,因为它包含并且管理应用对象的生命周期和配置。
你可以通过配置来设定你的Bean是单一实例,还是每次请求产生一个实例,并且设定它们之间的关联关系。
Spring有别于传统的重量级EJB容器,这些容器通常很大,很笨重。
Ø框架——Spring实现了使用简单的组件配置组合成一个复杂的应用。
在Spring中,应用中的对象是通过XML文件配置组合起来的。
并且Spring提供了很多基础功能(事务管理、持久层集成等),这使开发人员能够专注于开发应用逻辑。
总之,将Spring划分为这几个基本组件,所获得就是一个Spring框架,它能够帮助你开发出松耦合的应用代码,这些工作都是由Spring完成的,松耦合的优点(可维护性和可测试性)使得Spring更具有价值且应用范围更广。
1.1.2Spring框架构成
Spring框架是一个分层架构,由7个定义良好的模块组成。
Spring模块构建在核心容器之上,核心容器定义了创建、配置和管理bean的方式,如图1.1所示。
图1.1Spring框架结构
组成Spring框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。
每个模块的功能如下:
Ø核心容器:
核心容器提供Spring框架的基本功能。
核心容器的主要组件是BeanFactory,它是工厂模式的实现。
BeanFactory使用控制反转(IOC)模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
ØSpring上下文:
Spring上下文是一个配置文件,向Spring框架提供上下文信息。
Spring上下文包括企业服务,例如JNDI、EJB、电子邮件、国际化、校验和调度功能。
ØSpringAOP:
通过配置管理特性,SpringAOP模块直接将面向方面的编程功能集成到了Spring框架中。
所以,可以很容易地使Spring框架管理的任何对象支持AOP。
SpringAOP模块为基于Spring的应用程序中的对象提供了事务管理服务。
通过使用SpringAOP,不用依赖EJB组件,就可以将声明性事务管理集成到应用程序中。
ØSpringDAO:
JDBCDAO抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。
异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。
SpringDAO的面向JDBC的异常遵从通用的DAO异常层次结构。
ØSpringORM:
Spring框架插入了若干个ORM框架,从而提供了ORM的对象关系工具,其中包括JDO、Hibernate和iBatisSQLMap。
所有这些都遵从Spring的通用事务和DAO异常层次结构。
ØSpringWeb模块:
Web上下文模块建立在应用程序上下文模块之上,为基于Web的应用程序提供了上下文。
所以,Spring框架支持与JakartaStruts的集成。
Web模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
ØSpringMVC框架:
MVC框架是一个全功能的构建Web应用程序的MVC实现。
通过策略接口,MVC框架变成为高度可配置的,MVC容纳了大量视图技术,其中包括JSP、Velocity、Tiles、iText和POI。
Spring框架的功能可以用在任何J2EE服务器中,大多数功能也适用于不受管理的环境。
Spring的核心要点是:
支持不绑定到特定J2EE服务的可重用业务和数据访问对象。
毫无疑问,这样的对象可以在不同J2EE环境(Web或EJB)、独立应用程序、测试环境之间重用。
1.1.3Spring2.0的新特性
在Spring2.0发布附带的文档里面对2.0新特性做了概要的介绍,2.0的新特性是自然是我们最关注的方面:
一、Spring的XML配置引入XMLSchema语法简化配置
二、提供了request和session范围的bean
三、集成AspectJ,可以管理容器外对象,提供了领域模型的依赖注入
四、JPA支持
五、JDBC的NamedParameterJdbcTemplate
六、SpringWebMVC功能的大幅度扩充
七、支持动态语言ruby,groovy,beanshell
八、异步JMS支持,JMX支持,JCA支持的功能完善
因此Spring是一个野心很大的框架,从现在状况来看,Spring可以说是Java开源框架之集大成者,从未来来看,Spring将提供J2EE厂商所能够提供的所有必要的功能,最终Spring将有可能取J2EE规范而代之,成为Java企业开发的事实平台和事实标准。
总体来说,Spring2.0将向未来的宏大目标又迈进了一大步。
不过对于我等普通JavaWeb项目的开发需求来说,2.0的新特性也没有特别需要的。
1.2相关的Java基础知识
1.2.1类装载器ClassLoader
类装载器主是寻找类或接口字节码文件进行解析并构造JVM内部对象表示的组件。
在Java中,类装载器把一个类装入JVM中,要经过以下步骤:
1、装载:
查找和导入Class文件;
2、链接:
执行校验、准备和解析步骤,其中解析步骤是可以选择的;
校验:
检查载入Class文件数据的正确性;
准备:
给类的静态变量分配存储空间;
解析:
将符号引用转成直接引用;
3、初始化:
对类的静态变量、静态代码块执行初始化工作。
类装载工作由ClassLoader及其子类负责,ClassLoader是一个重要的Java运行时系统组件,它负责在运行时查找和装入Class字节文件。
1.2.2Java反射机制
Class反射对象描述语义结构,可以从Class对象中获取构造函数、成员变量、方法类等元素的反射对象,并以编程的方式通过这些反射对象对目标类对象进行操作。
这些反射对象类在java.reflect包中定义,下面最主要的三个反射类。
ØConstructor:
类的构造方法反射类,通过Class类getConstructors()方法可以获得类的所有构造方法反射对象数组。
Constructor的一个主要方法是newInstance(Object[]initargs),通过该方法可以创建一个对象类的实例,相当于new关键字。
ØMethod:
类方法的反射类,通过Class类getDeclaredMethods()方法可以获取类的所有方法反射类对象数组Method[]。
Method最主要的方法是invoke(Objectobj,Object[]args),obj表示操作的目标对象;args为方法入参。
此外,Method还有很多用于获取类方法更多信息的方法。
1)ClassgetReturnType():
获取方法的返回值类型;
2)Class[]getParameterTypes():
获取方法的入参类型数组;
3)Class[]getExceptionTypes():
获取方法的异常类型数组;
4)Annotation[][]getParameterAnnotations():
获取方法的注解信息。
ØField:
类的成员变量的反射类,通过Class类getDeclaredFields()方法可以获取类的成员变量的反射类,通过Class类getDeclaredFields(Stringname)方法刚可以获取某个特定名称的成员变量反射对象。
Fields类最主要的方法是set(Objectobj,Objectvalue),obj表示操作的目标对象,通过value为目标对象的成员变量设置值。
我们将从一个简单例子讲解Java反射机制,下面的Student类拥有两个构造方法、三个属性及多个常规方法。
具体代码如实例1所示:
实例1
packageorg.hopetech.reflect;
publicclassStudent{
privateStringname;
privateStringsex;
privateintage;
publicStudent(){
}
publicStudent(Stringname,Stringsex,intage){
this.name=name;
this.sex=sex;
this.age=age;
}
publicvoidintroduce(){
System.out.println("姓名:
"+name+"\n性别:
"+sex+"\n年龄:
"+age);
}
publicStringgetName(){
returnname;
}
publicvoidsetName(Stringname){
this.name=name;
}
publicStringgetSex(){
returnsex;
}
publicvoidsetSex(Stringsex){
this.sex=sex;
}
publicintgetAge(){
returnage;
}
publicvoidsetAge(intage){
this.age=age;
}
}
通常,我们会使用以下代码创建Student实例:
Studentstud=newStudent();
stud.setName(“张三”);
stud.setSex(“男”);
stud.setAge(22);
或
Studentstud=newStudent(“张三”,”男”,22);
上述两种方法都采用传统的直接调用目标类的方式进行操作,下面我们通过Java反射机制以一种更加通用的方式间接的操作目标类,具体代码如实例2所示:
实例2
packageorg.hopetech.reflect;
importjava.lang.reflect.Constructor;
importjava.lang.reflect.Method;
publicclassRelfectTest{
publicstaticStudentinitByDefaultConst()throwsThrowable{
ClassLoaderloader=Thread.currentThread().getContextClassLoader();
Classclazz=loader.loadClass("org.hopetech.reflect.Student");
Constructorcons=clazz.getConstructor((Class[])null);
Studentstud=(Student)cons.newInstance();
MethodsetName=clazz.getMethod("setName",String.class);
setName.invoke(stud,"张三");
MethodsetSex=clazz.getMethod("setSex",String.class);
setSex.invoke(stud,"男");
MethodsetAge=clazz.getMethod("setAge",int.class);
setAge.invoke(stud,22);
returnstud;
}
publicstaticvoidmain(String[]args)throwsThrowable{
Studentstud=initByDefaultConst();
stud.introduce();
}
}
实例2运行结果如图1.2所示:
图1.2实例2运行结果
1.3Java的反射和代理实现IOC模式
1.3.1关于IoC与DI的概念
首先想说说IoC(InversionofControl,控制倒转)。
这是spring的核心,贯穿始终。
所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。
这是什么意思呢,举个简单的例子,我们是如何找女朋友的?
常见的情况是,我们到处去看哪里有长得漂亮身材又好的女孩,然后打听她们的兴趣爱好、qq号、电话号、ip号、iq号………,想办法认识她们,投其所好送其所要,这个过程是复杂深奥的,我们必须自己设计和面对每个环节。
传统的程序开发也是如此,在一个对象中,如果要使用另外的对象,就必须得到它(自己new一个,或者从JNDI中查询一个),使用完之后还要将对象销毁(比如Connection等),对象始终会和其他的接口或类藕合起来。
那么IoC是如何做的呢?
有点像通过婚介找女朋友,在我和女朋友之间引入了一个第三者:
婚姻介绍所。
婚介管理了很多男男女女的资料,我可以向婚介提出一个列表,告诉它我想找个什么样的女朋友,比如长得像李嘉欣,身材像林熙雷,唱歌像周杰伦,速度像卡洛斯,技术像齐达内之类的,然后婚介就会按照我们的要求,提供一个女孩,我们只需要去和她谈恋爱、结婚就行了。
简单明了,如果婚介给我们的人选不符合要求,我们就会抛出异常。
整个过程不再由我自己控制,而是有婚介这样一个类似容器的机构来控制。
Spring所倡导的开发方式就是如此,所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。
所有的类的创建、销毁都由spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。
对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。
IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。
这一点是通过DI(DependencyInjection,依赖注入)来实现的。
比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了spring我们就只需要告诉spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。
在系统运行时,spring会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。
A需要依赖Connection才能正常运行,而这个Connection是由spring注入到A中的,依赖注入的名字就这么来的。
那么DI是如何实现的呢?
Java1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,spring就是通过反射来实现注入的。
1.3.2实现IoC
根据前面所举的例子,我用代码方式来解决找女朋友这个问题。
首先,我们必须设计一个Girl类,根据它可以构建多个不同类型的女孩来,具体的代码如实例3所示。
实例3
packageorg.hopetech.spring;
publicclassGirl{
privateStringname;//姓名
privateIntegerage;//年龄
privateBooleanbeautiful;//是否漂亮
publicGirl(){
}
publicIntegergetAge(){
returnage;
}
publicvoidsetAge(Integerage){
this.age=age;
}
publicBooleangetBeautiful(){
returnbeautiful;
}
publicvoidsetBeautiful(Booleanbeautiful){
this.beautiful=beautiful;
}
publicStringgetName(){
returnname;
}
publicvoidsetName(Stringname){
this.name=name;
}
}
然后,我们设计一个Boy类。
因为他需要约会,因此他依赖一个可以完成和他约会的女朋友。
当然,他也可以直接构建一个Girl对象,但这样一来,他的可选择性就只能是唯一的一个了。
因此,我们可以设定,他需要一个女朋友,而这个女朋友可以由婚介所来提供,这样他的可选择就大了很多。
具体代码如实例4所示。
实例4
packageorg.hopetech.spring;
publicclassBoy{
privateStringname;//姓名
privateGirlgirlFriend;//女朋友对象
publicBoy(){
}
publicvoidsetName(Stringname){
this.name=name;
}
/**
*注入女朋友
*
*@paramgirlFriend
*/
publicvoidsetGirlFriend(GirlgirlFriend){
this.girlFriend=girlFriend;
}
/**
*约会
*/
publicvoidmeetting(){
if(girlFriend.getAge()<20){
System.out.println("年龄没有20岁,"+name+"与"+girlFriend.getName()+"约会失败");
return;
}
if(!
girlFriend.getBeautiful()){
System.out.println("长的对不起观众"+name+"与"+girlFriend.getName()+"约会失败");
return;
}
System.out.println(name+"拉着"+girlFriend.getName()+"的手高兴的约会去了.");
}
}
婚介所拟定约会对象,制定了一个约会清单,具体的代码如实例5所示。
实例5
xmlversion="1.0"encoding="UTF-8"?
>
--凤姐的资料-->
--将凤姐设为曾哥的约会对象-->