spring培训笔记Word文档格式.docx
《spring培训笔记Word文档格式.docx》由会员分享,可在线阅读,更多相关《spring培训笔记Word文档格式.docx(32页珍藏版)》请在冰豆网上搜索。
![spring培训笔记Word文档格式.docx](https://file1.bdocx.com/fileroot1/2023-2/3/cc1ccc1f-6d6f-4bef-a69a-be90071fb60b/cc1ccc1f-6d6f-4bef-a69a-be90071fb60b1.gif)
资源管理21
JDBC21
IBATISSQL21
HIBERANTE21
DAO21
远程调用22
RMI22
EJB22
JAX-RPC22
Hessian22
Burlap22
Web层设计22
设计目标22
集成struts23
测试23
单元测试23
可测试性23
测试驱动开发24
覆盖率分析与其他测试工具24
性能与可伸缩性24
定义24
设置清晰的目标24
体系结构的选择25
不同实现的选择25
调优和部署25
一种循证的性能策略25
Spring开发25
最佳实践25
参考25
spring概述
Spring项目启动于2003年2月,其中基础代码来自《expertone-on-onej2eedesignanddevelopment》。
Spring是指一个用于构造Java应用程序的轻量级框架,不限定于只编写web应用,最少侵入。
Spring的核心是个轻量级(Lightweight)的容器(Container),它是实现IoC(InversionofControl)容器、非侵入性(Nointrusive)的框架,并提供AOP(Aspect-orientedprogramming)概念的实现方式,提供对持久层(Persistence)、事务(Transaction)的支持,提供MVCWeb框架的实现,并对一些常用的企业服务API(ApplicationInterface)提供一致的模型封装,是一个全方位的应用程序框架(Applicationframework),除此之外,对于现存的各种框架(Struts、JSF、Hibernate等),Spring也提供了与它们相整合的方案。
本质上讲,Spring是IOC(InversionofControl)和面向切面编程(AOP)的组合体。
它是一个非侵入式的框架,增强了POJO的功能。
从服务上讲(Withaserviceabstraction),它将程序代码从J2EE环境解耦到普通的java对象(自然,这些代码可以脱离J2EE而在多种环境中运行)。
它还在很多功能上提供了除EJB之外的选择――比如为所有的POJO提供声明式事务。
Spring被广泛运用到很多项目中,从小的web程序到大的企业应用程序。
控制反转,还是依赖注入
Spring框架的核心基于“控制反转(InversionofControl,IoC)”原理。
IoC是一种将组件依赖关系的创建和管理置于程序外部的技术。
假设一个例子,类Foo依赖于类Bar的一个实例来进行某些操作。
传统的方式,Foo使用new操作符创建一个Bar的实例,或者通过某种工厂类来获得。
使用IoC方法,Bar的一个实例(或者其子类的实例)是通过某些外部处理过程在运行时动态传递给Foo的。
这种在运行时注入依赖的行为方式,使得IoC后来被改称为另一个含义更明确的名字:
“依赖注入(DependencyInjection,DI)”。
Spring的DI实现是基于两个Java核心概念:
JavaBean和Interface。
当你使用DI的时候,你可以使得依赖配置与你的代码保持隔离。
JavaBeans提供了一种创建Java资源的标准方法,并且这些资源是可以通过标准方式配置的。
接口与DI是相互受益的技术,针对接口设计与编程有助于应用程序的灵活性,但要把采用接口设计的应用程序的各部分连接起来,其复杂度非常高,并且给开发者带来了额外的编码负担。
通过采用DI,为基于接口的设计而编写的辅助代码大大减少了,近乎于零。
反过来,通过采用接口,你可以获得DI的最大好处,因为你的bean可以采用任何满足其依赖的接口实现。
减少“粘合”代码:
DI带来的最大的好处之一就是,它可以奇迹般地消除你为了连接应用程序的各个部件而编写的大量代码。
这些代码常常很简单而且琐碎——通过构造一个新的对象来创建依赖。
然而,当你需要从JNDI库中查询依赖,或者这些调用不能直接通过调用产生时(比如访问远程资源),这些琐碎的代码可能会变得相当复杂。
在这些情形下,DI真的可以简化粘合代码,因为它提供了自动的JNDI查询,以及对远程资源的自动代理。
依赖外置化:
你可以将依赖的配置外置,这样你可以无需重新编译代码就重新配置。
这给你带来两个有趣的好处。
首先,Spring中的DI是一种理想的配置方式,可以让你在外部自由的配置应用程序的所有选项。
其次,依赖外置使得在不同的实现间切换变得非常容易。
假设你有一个DAO组件,它针对PostgreSQL数据库进行数据操作,你想把它升级到Oracle。
使用DI,你可以简单地重新配置你的业务对象的依赖关系,让它使用Oracle实现而非PostgreSQL实现。
在统一的地方管理依赖:
采用传统的方式管理依赖时,你在任何需要的地方创建依赖的对象实例——就在依赖者的内部。
在大部分简单的程序中,你会让依赖关系在你的代码中散播,改变它们通常会带来问题。
当使用DI的时候,所有关于依赖的信息都通过一个简单的库进行管理,使得管理依赖变得既简单又不容易出错。
提高可测试性:
当面向DI设计你的类时,你就可以便捷地替换依赖。
当在测试程序的时候,这特别有用。
假设一个业务对象进行某些复杂的处理,其中一部分它使用一个DAO对象来访问存储在关系数据库中的对象。
在测试的时候,你对测试DAO本身不感兴趣,你只是简单地希望采用不同的数据集来测试你的业务对象。
在通常的方式中,你的测试会变得很痛苦,因为你无法简单地把这个DAO实现替换为一个模拟实现,模拟返回你的测试数据。
相反,你需要确认你的测试数据库中包含正确的数据,为你的测试使用完整的DAO实现。
使用DI的话,你可以为你的DAO对象创建一个模拟实现,然后把它传递给你的业务对象进行测试。
这种机制可以扩展到测试你的应用的任何一层,对测试web组件特别有用,你可以创建HttpServletRequest和HttpServeletResponse的模仿实现。
鼓励良好的程序设计:
针对DI设计意味着整体上针对接口设计。
典型的基于注入的应用程序,其所有的主要组件都是定义为接口的,然后这些接口的具体实现采用DI容器创建并糅合到一起。
在DI和基于DI的容器比如Spring出现之前,这种设计在Java中就是可行的,但是通过使用Spring,你免费获得了一个包含完整DI功能的基础,你可以集中精力于建立你的业务逻辑,而非关注支持业务的框架。
面向方面编程
AOP提供了实现横切逻辑的功能——这一逻辑应用于你应用程序的很多地方,只需要编写一次,就可以把这一逻辑自动在整个应用中实施目前有两种主流的AOP实现。
静态AOP,比如AspectJ(www.apsectj.org),提供了编译器的方法来构建基于AOP的逻辑,并把它加入到应用程序中。
动态AOP,比如Spring中的这样,允许在运行时把横切逻辑应用到任意一段代码中。
两种不同的AOP方法都有其适用面,实际上,AOP提供了与AspectJ整合的功能。
一致性抽象
异常
资源管理
事务管理
事务管理Spring提供了极好的事务管理抽象层,允许你进行编程式或者声明式事务控制。
通过使用Spring的抽象层来进行事务,你可以很容易地抽换底层的数据访问协议和资源管理方式。
你可以从一个简单的、本地的、单资源的事务管理起步,转移到全局的、多资源的事务管理环境,而无需更改你的代码。
测试
与其它框架整合
在Spring中访问数据
Spring中的JDBC支持使得编写基于JDBC的应用变得更加现实了,就算是比较复杂的应用程序也行。
对Hibernate、iBATIS和JDO的支持让本就简单的API更加简化,减轻了开发者的负担,易于在同一程序中混合不同的数据访问技术。
简化与整合J2EE
Web层的MVC
远程访问(Remoting)支持
Mail支持
计划任务支持
简化的异常处理
源代码级的Metadata(元数据)
控制反转
容器(container)
是指应用代码的运行框架。
提供服务:
生命周期管理
查找服务
配置服务
依赖决议
理想容器特点:
可接插性
一致性
一站式购物
提供企业级服务
IoC类型
type
name
description
Type1
Interfacedependent
Beansmustimplementspecificinterfacestohavetheirdependenciesmanagedbythecontainer.
Type2
Setterinjection
Dependenciesandpropertiesareconfiguredthroughabean’ssettermethods.
Type3
Constructorinjection
Dependenciesandpropertiesareconfiguredthroughthebean’sconstructor.
依赖注入的基本原则:
应用对象不应该负责查找资源或者其他依赖的协作组件。
配置对象的工作应该由IoC容器完成,“查找资源”的逻辑应该从应用代码中抽取出来,交给容器负责。
接口方法注入:
组件通过接口的方式完成注入,具备侵入性,它要求组件必须与特定的接口相关联,因此并不被看好,实际使用有限。
设值方法注入:
组件通过javabean属性来表达自己需要配置的值和依赖的对象,建立在javabean规范之上。
构造子注入:
组件以构造子参数的形式描述自己需要的依赖关系。
IoCinspring
Spring的IoC控件主要专注于如何利用classes、对象和服务去组成一个企业级应用,通过规范的方式,将各种不同的控件整合成一个完整的应用。
Spring中使用了很多被实践证明的最佳实践和正规的设计模式,并且进行了编码实现。
如果你是一个,构架师或者开发人员完全可以取出它们集成到你自己的应用之中。
这对于那些使用了SpringFramework的组织和机构来说,在spring基础上实现应用不仅可以构建优秀的,可维护的应用并对Spring的设计进行验证,确实是一件好事情。
Spring框架所提供的众多功能之所以能成为一个整体正是建立在IoC的基础之上,org.springframework.beans及org.springframework.context包是SpringIoC容器的基础。
BeanFactory提供的高级配置机制,使得管理任何性质的对象成为可能。
ApplicationContext是BeanFactory的扩展,功能得到了进一步增强,比如更易与SpringAOP集成、消息资源处理(国际化处理)、事件传递及各种不同应用层的context实现(如针对web应用的WebApplicationContext)。
简而言之,BeanFactory提供了配制框架及基本功能,而ApplicationContext则增加了更多支持企业核心内容的功能。
ApplicationContext完全由BeanFactory扩展而来,因而BeanFactory所具备的能力和行为也适用于ApplicationContext。
●用户注册的例子
我们先看看更进一步的需求:
实现一个用户注册信息持久化的类。
功能:
1、保存用户注册的信息;
2、根据用户的名称获得该注册用户。
虽然功能简单,但它对持久化方式的要求却非常的灵活:
1、在内存中持久化,供测试、演示使用。
2、如果用户的数据很少,将用户信息持据化到文本文件中。
3、如果用户信息很多,并需要一些灵活的查询,则需要使用JDBC技术将用将用户信息持久化到数据库中。
4、面对企业复杂关联的数据,甚至需要使用持久层框架来实现用户信息的持久化,比如:
iBATIS、Hibernate等。
如何去设计、实现我们这个持久化类呢?
我们遵循软件开发的原则“首先让它跑起来,再去优化(重构)它”,我们首先实现最简单的在内存中持久化用户信息。
既然我们要保存和取得用户信息,首先应该设计用户类。
代码如下:
User.java
publicclassUser{
privateLongid;
privateStringname;
privateStringpassword;
privateStringgroup;
publicUser(Stringname,Stringpassword){
this.name=name;
this.password=password;
}
//相应的get/set方法
………..
持久化类有两个方法,分别在内存中保存和获取User对象。
MemoryUserPersist.java
publicclassMemoryUserPersist{
privatestaticMapusers=newHashMap();
static{
UserdefaultAdmin=newUser("
Moxie"
"
pass"
);
users.put(defaultAdmin.getName(),defaultAdmin);
}
publicMemoryUserPersist(){
publicvoidsaveUser(Useruser){
users.put(user.getName(),user);
publicUserLoadUser(StringuserName){
return(User)users.get(userName);
用户持久化类完成之后,我们就可以在客户端UserRegister中使用它了。
例如:
用户注册时,UserRegister代码片断如下:
MemoryUserPersistuserPersist=newMemoryUserPersist();
userPersist.saveUser(user);
可是,现在如果要在文本文件中持久化User,又该如何实现呢?
实现一个TextUserPersist类,这个并不困难。
但客户端代码将面临重大灾难:
找到所有使用过MemoryUserPersist的客户端类,将他们中的MemoryUserPersist逐个手工修改为TextUserPersist,并且重新编译,当然以前的测试也必须全部从头来过!
人生的浩劫只是刚刚开始,因为根据前面的需求我们至少要分别实现四种持久化方式!
这时,你一定和我一样在期待着救世主的早日降临——接口(Interface)。
●面向接口编程
什么是接口?
☐接口定义了行为的协议,这些行为在继承接口的类中实现。
☐接口定义了很多方法,但是没有实现它们。
类履行接口协议并实现所有定义在接口中的方法。
☐接口是一种只有声明没有实现的特殊类。
接口的优点:
☐Client不必知道其使用对象的具体所属类。
☐一个对象可以很容易地被(实现了相同接口的)的另一个对象所替换。
☐对象间的连接不必硬绑定(hardwire)到一个具体类的对象上,因此增加了灵活性。
☐松散藕合(loosenscoupling)。
☐增加了重用的可能性。
接口的缺点:
设计的复杂性略有增加
●重构第一步——面向接口编程
1、设计用户持久化类的接口UserDao,代码如下:
publicinterfaceUserDao{
publicvoidsave(Useruser);
publicUserload(Stringname);
2、具体的持久化来必须要继承UserDao接口,并实现它的所有方法。
我们还是首先实现内存持久化的用户类:
publicclassMemoryUserDaoimplementsUserDao{
;
Useruser=newUser("
publicvoidsave(Useruser){
users.put(user.getId(),user);
publicUserload(Stringname){
return(User)users.get(name);
MemoryUserDao的实现代码和上面的MemoryUserPersist基本相同,唯一区别是MemoryUserDao类继承了UserDao接口,它的save()和load()方法是实现接口的方法。
这时,客户端UserRegister的代码又该如何实现呢?
UserDaouserDao=newMemoryUserDao();
userDao.save(user);
(注:
面向对象“多态”的阐述)
如果我们再切换到文本的持久化实现TextUserDao,客户端代码仍然需要手工修改。
虽然我们已经使用了面向对象的多态技术,对象userDao方法的执行都是针对接口的调用,但userDao对象的创建却依赖于具体的实现类,比如上面MemoryUserDao。
这样我们并没有完全实现前面所说的“Client不必知道其使用对象的具体所属类”。
如何解决客户端对象依赖具体实现类的问题呢?
下面该是我们的工厂(Factory)模式出场了!
●重构第二步――工厂模式
我们使用一个工厂类来实现userDao对象的创建,这样客户端只要知道这一个工厂类就可以了,不用依赖任何具体的UserDao实现。
创建userDao对象的工厂类UserDaoFactory代码如下:
publicclassUserDaoFactory{
publicstaticUserDaocreateUserDao(){
returnnewMemoryUserDao();
客户端UserRegister代码片断如下:
UserDaouserDao=UserDaoFactory.CreateUserDao();
现在如果再要更换持久化方式,比如使用文本文件持久化用户信息。
就算有再多的客户代码调用了用户持久化对象我们都不用担心了。
因为客户端和用户持久化对象的具体实现完全解耦。
我们唯一要修改的只是一个UserDaoFactory类。
●重构第三步——工厂(Factory)模式的改进
到这里人生的浩劫已经得到了拯救。
但我们仍不满足,因为假如将内存持久化改为文本文件持久化仍然有着硬编码的存在——UserDaoFactory类的修改。
代码的修改就意味着重新编译、打包、部署甚至引入新的Bug。
所以,我们不满足,因为它还不够完美!
如何才是我们心目中的完美方案?
至少要消除更换持久化方式时带来的硬编码。
具体实现类的可配置不正是我们需要的吗?
我们在一个属性文件中配置UserDao的实现类,例如:
在属性文件中可以这样配置:
userDao=com.test.MemoryUserDao。
UserDao的工厂类将从这个属性文件中取得UserDao实现类的全名,再通过Class.forName(className).newInstance()语句来自动创建一个UserDao接口的具体实例。
UserDaoFactory代码如下:
StringclassName="
"
//……从属性文件中取得这个UserDao的实现类全名。
UserDaouserDao=null;
try{
userDao=(UserDao)Class.forName(className).newInstance();
}catch(Exceptione){
e.printStackTrace();
}
returnuserDao;
通过对工厂模式的优化,我们的方案已近乎完美。
如果现在要更换持久化方式,不需要再做任何的手工编码,只要修改配置文件中的userDao实现类名,将它设置为你需要更换的持久化类名即可。
我们终于可以松下一口气了?
不,矛盾仍然存在。
我们引入了接口,引入了工厂模式,让我们的系统高度的灵活和可配置,同时也给开发带来了一些复杂度:
1、本来只有一个实现类,后来却要为这个实现类引入了一个接口。
2、引入了一个接口,却还需要额外开发一个对应的工厂类。
3、工厂类过多时,管理、维护非常困难。
比如:
当UserDao的实现类是JdbcUserDao,它使用JDBC技术来实现用户信息从持久化。
也许要在取得JdbcUserDao实例时传入数据库Connection,这是仍少UserDaoFactory的硬编码。
当然,面接口编程是实现软件的可维护性和可重用行的重要原则已经勿庸置疑。
这样,第一个复杂度问题是无法避免的,再说一个接口的开发和维护的工作量是微不足道的。
但后面两个复杂度的问题,我们是完全可以解决的:
工厂模式的终极方案——IoC模式。
●重构第四步――IoC容器
使用IoC容器,用户注册类UserRegister不用主动创建UserDao实现类的实例。
由IoC容器主动创建UserDao实现类的实例,并注入到用户注册类中。
我们下面将使用Spring提供的IoC容器来管理我们的用户注册类。
用户注册类UserRegister的部分代码如下:
publicclassUserRegister{
privateUserDaouserDao=null;
//由容器注入的实例对象
publicvoidsetUserDao(UserDaouserDao){
this.userDao=userDao;
//UserRegister的业务方法
在其它的UserRegister方法中就可以直接使用userDao对象了,它的实例由Spring容器主动为它创建。
但是,如何组装一个UserDao的实现类到UserRegister中呢?
哦,Spring提供了配置文件来组装我们的组件。
Spring的配