1、学习比较Spring In Action 学习笔记 1 Spring 基础基础 在本部分,将介绍 Spring 框架的两个核心特性:反向控制(IoC)和面向切面编程(AOP)。?首先,简单介绍 Spring 中 IoC 和 AOP;?其次,装配 Bean,介绍如何利用 IoC 实现系统对象间的松耦合关联,如何使用 XML在 Spring 容器中定义系统对象,装配其依赖类。?创建切面,介绍 Spring 的 AOP 把系统级服务(如安全和监控)从被服务对象中解耦出来 1.1 Spring 简介简介 1.1.1 Spring 特点特点 Spring 是一个轻量级的 IoC 和 AOP 容器框架。?
2、轻量级:从大小及系统开支上说。且 Spring 是非侵入式的(基于 Spring 开发的系统中对象一般不依赖于 Spring 的类)?反向控制:使用 IoC 对象是被动接收依赖类而不是主动去找(容器在实例化对象时主动将其依赖类注入给它)。?面向切面:将业务逻辑从系统服务中分离,实现内聚开发。系统对象只做其该做的业务逻辑不负责其他系统问题(如日志和事务支持)。?容器:包含且管理系统对象的生命周期和配置,通过配置设定 Bean 是单一实例还是每次请求产生一个实例,并设定 Bean 之间的关联关系?框架:使用简单组件配置组合成一个复杂的系统,系统中的对象是通过 XML 文件配置组合起来的,且 Spr
3、ing 提供了很多基础功能(事务管理、持久层集成等)1.1.2 Spring 模块模块 图 1-1 Spring 框架由 7 个模块组成(如图 1-1):?核心容器:提供了基础功能。包含 BeanFactory 类(Spring 框架的核心,采用工厂1 学习笔记系列学习比较Spring In Action 学习笔记 模式实现 IoC)?应用上下文模块:扩展了 BeanFactory,添加了对 I18N(国际化)、系统生命周期事件及验证的支持,并提供许多企业级服务,如电子邮件服务、JNDI 访问、EJB集成、远程调用及定时服务,并支持与模板框架(如 Velocity 和 FreeMarker)的
4、集成?AOP 模块:对面向切面提供了丰富的支持,是 Spring 应用系统开发切面的基础;并引入 metadata 编程?JDBC 和 DAO 模块:?O/R 映射模块:?Web 模块:建立在应用上下文模块的基础上,提供了适合 web 系统的上下文,另外,该模块支持多项面向 web 的任务,如透明处理多文件上传请求,自动将请求参数绑定到业务对象中等?MVC 框架:所有模块都是建立在核心容器之上的,容器规定如何创建、配置和管理 Bean,以及其它细节 1.2 示例示例 1.2.1 Spring 简单示例简单示例 程序清单 1.1 GreetingService 接口将实现从接口中分离出来 pac
5、kagepackage com.springinaction.chapter01.hello;publicpublic interfaceinterface GreetingService publicpublic voidvoid sayGreeting();程序清单 1.2 GreetingServiceImpl.java 负责打印问候语 packagepackage com.springinaction.chapter01.hello;publicpublic classclass GreetingServiceImpl implementsimplements GreetingServ
6、ice privateprivate String greeting;publicpublic GreetingServiceImpl()publicpublic GreetingServiceImpl(String greeting)thisthis.greeting=greeting;publicpublic voidvoid sayGreeting()System.out.println(greeting);publicpublic voidvoid setGreeting(String greeting)thisthis.greeting=greeting;2 学习笔记系列学习比较Sp
7、ring In Action 学习笔记 程序清单 1.3 在 Spring 中配置 Hello World Buenos Dias!程序清单 1.4 Hello World 示例的主类 packagepackage com.springinaction.chapter01.hello;importimport org.springframework.beans.factory.BeanFactory;importimport org.springframework.beans.factory.xml.XmlBeanFactory;importimport org.springframework
8、.core.io.FileSystemResource;importimport org.springframework.core.io.Resource;publicpublic classclass HelloApp publicpublic staticstatic voidvoid main(String args)throwsthrows Exception Resource resource=newnew FileSystemResource(hello.xml);BeanFactory factory=newnew XmlBeanFactory(resource);Greetin
9、gService greetingService=(GreetingService)factory.getBean(greetingService);greetingService.sayGreeting();1.2.2 IoC 示例示例 使用 IoC,对象的依赖都是在对象创建时由负责协调系统中各个对象的外部实体提供的。减少耦合的一个通常做法是将具体实现隐藏在接口下,使得具体实现类的替换不会影响到引用类。程序清单 1.5 Quest.java(使用接口解耦合)packagepackage com.springinaction.chapter01.knight;publicpublic inte
10、rfaceinterface Quest publicpublic abstractabstract Object embark()throwsthrows QuestException;3 学习笔记系列学习比较Spring In Action 学习笔记 程序清单 1.6 HolyGrailQuest.java(Quest 的实现类)packagepackage com.springinaction.chapter01.knight;publicpublic classclass HolyGrailQuest implementsimplements Quest publicpublic Ho
11、lyGrailQuest()publicpublic Object embark()throwsthrows QuestException /Do whatever it means to embark on a quest returnreturn newnew HolyGrail();程序清单 1.7 Knight.java(使用接口解耦合)packagepackage com.springinaction.chapter01.knight;publicpublic interfaceinterface Knight publicpublic Object embarkOnQuest()t
12、hrowsthrows QuestException;publicpublic String getName();程序清单 1.8 KnightOfTheRoundTable.java(Knight 的实现类,骑士被动获取探险任务)packagepackage com.springinaction.chapter01.knight;publicpublic classclass KnightOfTheRoundTable implementsimplements Knight privateprivate String name;privateprivate Quest quest;publi
13、cpublic KnightOfTheRoundTable(String name)thisthis.name=name;publicpublic Object embarkOnQuest()throwsthrows QuestException System.out.println(ceshi);returnreturn quest.embark();publicpublic voidvoid setQuest(Quest quest)thisthis.quest=quest;publicpublic String getName()returnreturn thisthis.name;4
14、学习笔记系列学习比较Spring In Action 学习笔记 程序清单 1.9 knight.xml(将一个探险任务装配给一个骑士)Bedivere 程序清单 1.10 KnightApp.java(运行 Knight 例子)packagepackage com.springinaction.chapter01.knight;importimport org.springframework.beans.factory.BeanFactory;importimport org.springframework.beans.factory.xml.XmlBeanFactory;importimpo
15、rt org.springframework.core.io.FileSystemResource;importimport org.springframework.core.io.Resource;publicpublic classclass KnightApp publicpublic staticstatic voidvoid main(String args)throwsthrows Exception Resource resource=newnew FileSystemResource(knight.xml);BeanFactory factory=newnew XmlBeanF
16、actory(resource);Knight knight=(Knight)factory.getBean(knight);knight.embarkOnQuest();程序清单 1.11 KnightOfTheRoundTableTest.java(KnightOfTheRoundTable 的测试类)packagepackage com.springinaction.chapter01.knight;importimport junit.framework.TestCase;publicpublic classclass KnightOfTheRoundTableTest extends
17、extends TestCase publicpublic voidvoid testEmbarkOnQuest()KnightOfTheRoundTable knight=newnew KnightOfTheRoundTable(Bedivere);Quest quest=newnew HolyGrailQuest();5 学习笔记系列学习比较Spring In Action 学习笔记 knight.setQuest(quest);HolyGrail grail;trytry grail=(HolyGrail)knight.embarkOnQuest();assertNotNull(grai
18、l);assertTrue(grail.isHoly();catchcatch(QuestException e)e.printStackTrace();1.2.3 AOP 1.2.3.1 AOP 简介简介 AOP 常被定义为一种编程结束,用来在系统中提升业务的分离。图 1-4 系统业务(如日志和安全)的调用常分散在各个模块中,而这些业务并不是该模块的主要业务 图 1-5 使用 AOP,系统业务覆盖了它影响到的组件 6 学习笔记系列学习比较Spring In Action 学习笔记 1.2.3.2 AOP 示例示例 程序清单 1.12 MinstrelAdvice.java(面向切面的吟游诗人
19、)packagepackage com.springinaction.chapter01.knight;importimport java.lang.reflect.Method;importimport org.apache.log4j.Logger;importimport org.springframework.aop.MethodBeforeAdvice;publicpublic classclass MinstrelAdvice implementsimplements MethodBeforeAdvice publicpublic voidvoid before(Method me
20、thod,Object args,Object target)throwsthrows Throwable Knight knight=(Knight)target;/得到目标类的logger Logger song=Logger.getLogger(target.getClass();song.debug(Brave +knight.getName()+did +method.getName();修改knight.xml 编织切面,将 MinstrelAdvice 装配到 Knight 中 程序清单 1.13 knight.xml(将 MinstrelAdvice 装配到 Knight 中)
21、Bedivere 7 学习笔记系列学习比较Spring In Action 学习笔记 com.springinaction.chapter01.knight.Knight minstrel 图 1-7 面向切面的吟游诗人覆盖了骑士,在骑士没有察觉的情况下记录骑士的行动 1.3 Spring 比较比较 1.3.1 比较比较 Spring 和和 EJB 表 1.1 Spring 和 EJB 功能比较 特征特征 EJB Spring 事务管理?必须使用 JTA 事务管理器?支持跨越远程调用的事务?通过 PlatformTransactionManager 接口?自身不支持分布式事务需使用 JTA 声
22、明式事务支持?可使用部署文件声明式定义事务?可通配符*对每个方法或每个类定义事务行为?不能声明式定义回滚动作只能采用编码方式实现?可通过 Spring 配置文件或类元数据定义声明式事务?可显示或使用正则表达式为方法定义事务行为?可声明式地为每个方法和每个异常类型定义回滚动作 持久化?支持编码式 BMP 和声明式CMP?提 供 一 个 集 成 多 种 持 久 化 技 术 的 框 架(JDBC、Hibernate、JDO 和 iBATIS)声明式安全?支持基于用户和角色的声明式安全管理,对用户和角色的管理和实现是容器特定的?在部署描述中声明安全配置?没有自己的安全实现?Acegi,建立在 Spri
23、ng 基础之上的开源安全框架,提供了通过配置文件和类元数据配置的声明式安全服务 分布计算?提供容器管理的远程方法调用?通过 RMI、JAX-RPC 和 WebService 提供远程调用代理 8 学习笔记系列学习比较Spring In Action 学习笔记 1.3.2 关于其他轻量级容器关于其他轻量级容器 表 1.2 IoC 类型 类型类型 名称名称 描述描述 类型 1 接口依赖 Beans 必须实现指定的接口,这样它的依赖类才能被容器管理 类型 2 设值注入 依赖类和属性通过 Bean 的 setter 方法注入 类型 3 构造注入 依赖类和属性通过 Bean 的构造方法注入 2 装配装配
24、 bean Spring 提供两种不同的容器:?Bean 工厂(由 org.springframework.beans.factory.BeanFactory 接口定义)是最简单的容器,提供基础的依赖注入支持?应用上下文(由 org.springframework.context.ApplicationContext 接口定义)建立在bean 工厂基础之上,提供系统构架服务。2.1 简介简介 2.1.1 BeanFactory 介绍介绍 2.1.2 应用上下文应用上下文 应用上下文不仅载入 Bean 定义信息,装配 Bean,根据需要分发 Bean,还提供如下功能:?文本信息解析工具,包括对国
25、际化的支持?载入文件资源的通用方法?向注册为监听器的 Bean 发送事件 常用实现类如下:?ClassPathXmlApplicationContext:从类路径中 XML 文件载入上下文定义信息,把上下文定义文件当成类路径资源;(在类路径中寻找)?FileSystemXmlApplicationContext:从文件系统中的 XML 文件载入上下文定义信息(在指定的路径中寻找)?XmlWebApplicationContext:从 web 系统中的 XML 文件载入上下文定义信息 2.1.3 Bean 的生命的生命 如图 2.1 所示,在 BeanFactory 中 Bean 的生命周期:1
26、.容器寻找 Bean 的定义信息并将其实例化 2.使用依赖注入,Spring 按照 Bean 定义信息配置 Bean 的所有属性 9 学习笔记系列学习比较Spring In Action 学习笔记 3.如果 Bean 实现了 BeanNameAware 接口,工厂调用 Bean 的 setBeanName()方法传递 Bean 的 ID 4.如果 Bean 实现了 BeanFactoryAware 接口,工厂调用 setBeanFactory()方法传入工厂自身 5.如果有 BeanPostProcessor 和 Bean 关联,那么其 postProcessBeforeInitializat
27、ion()方法将被调用 6.如果 Bean 指定了 init-method 方法,将被调用 7.最后,如果有 BeanPostProcessor 和 Bean 关联,那么其 postProcessAfterInitialization()方法将被调用 此时,Bean 已经可以被应用系统使用,并将被保留在 BeanFactory 中直到它不在被需要。有两种方法可将其从 BeanFactory 中删除掉。1.如 Bean 实现了 DisposableBean 接口,destroy()方法被调用 2.如指定了定制的销毁方法,就调用这个方法 如图 2.2 所示,Bean 在 Spring 应用上下文中
28、的生命周期与在 BeanFactory 中的生命周期只有一点不同:如 Bean 实现了 ApplicationContextAware 接口,setApplicationContext()方法会被调用。10 学习笔记系列学习比较Spring In Action 学习笔记 2.2 基本装配基本装配 2.2.1 示例示例 11 学习笔记系列学习比较Spring In Action 学习笔记 程序清单 2.1 StudentService.java packagepackage chapter02;publicpublic interfaceinterface StudentService publ
29、icpublic Student getStudent(String id);publicpublic voidvoid createStudent(Student student);publicpublic java.util.Set getCompletedCourses(Student student);程序清单 2.2 CourseService.java packagepackage chapter02;publicpublic interfaceinterface CourseService publicpublic Course getCourse(String id);publ
30、icpublic voidvoid createCourse(Course course);publicpublic java.util.Set getAllCourses();publicpublic voidvoid enrollStudentInCourse(Course course,Student student)throwsthrows CourseException;程序清单 2.3 StudentServiceImpl.java(StudentService 处理有关学生的功能)packagepackage chapter02;importimport java.util.Se
31、t;publicpublic classclass StudentServiceImpl implementsimplements StudentService privateprivate StudentDao studentDao;publicpublic StudentServiceImpl(StudentDao studentDao)/通过构造方法注入 thisthis.studentDao=studentDao;publicpublic voidvoid setStudentDao(StudentDao studentDao)/或通过setter方法注入 thisthis.stude
32、ntDao=studentDao;publicpublic voidvoid createStudent(Student student)studentDao.create(student);publicpublic Set getCompletedCourses(Student student)returnreturn studentDao.getCompletedCourses(student);publicpublic Student getStudent(String id)returnreturn studentDao.findById(id);12 学习笔记系列学习比较Spring
33、 In Action 学习笔记 程序清单 2.4 StudentDao.java(StudentServiceImpl 将其很多职责委托给了 StudentDao)packagepackage chapter02;importimport java.util.Set;/*处理与数据库的交互来读写学生信息 */publicpublic interfaceinterface StudentDao Student findById(String id);voidvoid create(Student student);Set getCompletedCourses(Student student);
34、程序清单 2.5 CourseServiceImpl.java packagepackage chapter02;importimport java.util.Iterator;importimport java.util.Set;publicpublic classclass CourseServiceImpl implementsimplements CourseService privateprivate CourseDao courseDao;privateprivate StudentService studentService;privateprivate intint maxSt
35、udents;publicpublic CourseServiceImpl(CourseDao dao)/通过构造方法注入设置CourseDao thisthis.courseDao=dao;publicpublic voidvoid setStudentService(StudentService service)/通过setter方法设置 thisthis.studentService=service;publicpublic voidvoid setMaxStudents(intint maxStudents)thisthis.maxStudents=maxStudents;public
36、public intint getMaxStudents()returnreturn maxStudents;publicpublic Course getCourse(String id)returnreturn courseDao.findById(id);publicpublic voidvoid createCourse(Course course)courseDao.create(course);publicpublic voidvoid enrollStudentInCourse(Course course,Student student)throwsthrows CourseEx
37、ception /在学生可以登记一门课之前,必须已修完所有预修课程 ifif(course.getStudents().size()=maxStudents)throwthrow newnew CourseException(Course is full);13 学习笔记系列学习比较Spring In Action 学习笔记 /判断学生是否满足先决条件 enforcePrerequisites(course,student);course.getStudents().add(student);courseDao.update(course);privateprivate voidvoid en
38、forcePrerequisites(Course course,Student student)throwsthrows CourseException Set completed=studentService.getCompletedCourses(student);Set prereqs=course.getPrerequisites();forfor(Iterator iter=prereqs.iterator();iter.hasNext();)ifif(!completed.contains(iter.next()/如果学生不满足先决条件,抛出异常 throwthrow newne
39、w CourseException(Prerequisites are not met.);publicpublic Set getAllCourses()returnreturn courseDao.getAllCourses();程序清单 2.6 CourseDao.java(CourseServiceImpl 将其很多职责委托给了 CourseDao)packagepackage chapter02;importimport java.util.Set;publicpublic interfaceinterface CourseDao voidvoid update(Course cou
40、rse);Course findById(String id);voidvoid create(Course course);Set getAllCourses();程序清单 2.7 training-dao.xml(向容器中装配 DAO Bean)14 学习笔记系列学习比较Spring In Action 学习笔记 程序清单 2.8 training-service.xml(向容器中装配逻辑层 Bean)!-通过子元素设置基本类型属性-30 !-通过子元素设置指向其他Bean的属性-2.2.2 其他说明其他说明 2.2.2.1 原型与单实例原型与单实例 Spring 中的 Bean 缺省情况
41、下是单实例模式。的 singleton 属性告诉上下文该 Bean是否为单实例 Bean,缺省是 true,为 false 表示该 Bean 为原型 Bean。15 学习笔记系列学习比较Spring In Action 学习笔记 2.2.2.2 实例化与销毁实例化与销毁 Bean 实例化时,有时需要做些初始化工作,然后才能使用;同样,当 Bean 从容器删除时需要按顺序做些清理工作。在 Bean 的定义中设置 init-method,此方法在 Bean 被实例化时被调用;同样,也可设置destory-method,该方法在 Bean 从容器删除之前被调用。2.2.2.3 内部内部 Bean 程
42、序清单 2.9 training-nest.xml(使用内部 Bean 设置属性)!-通过子元素设置基本类型属性-30 16 学习笔记系列学习比较Spring In Action 学习笔记 2.2.2.4 装配集合装配集合 表 2.1 Spring 装配支持的集合类型 XML 类型 java.awt.List,arrays java.awt.Set java.awt.Map java.awt.Properties 2.2.2.4.1 示例示例 程序清单 2.10(装配集合)example/ex1.hbm.xml example/ex2.hbm.xml$hibernate.dialect tru
43、e 20 20 true 17 学习笔记系列学习比较Spring In Action 学习笔记 2.2.2.4.2 装配装配 List 和数组和数组 2.2.2.4.3 装配装配 Set 2.2.2.4.4 装配装配 Map Map 中的每条条目是由一个主键和一个数值组成的,用元素来定义一条条目。Map 中的的数值和及的一样,可以是任何有效的属性元素,包括、等;注意的是,配置时,属性 key 的值只能是 String。2.2.2.4.5 装配装配 Properties Java.util.Properties 集合是最后一个能在 Spring 中装配的集合类,使用元素来装配。使用元素表示每条熟
44、悉。但的值只能是 String 型的。18 学习笔记系列学习比较Spring In Action 学习笔记 程序清单 2.11(使用进行 URL 映射)viewCourseController 2.2.2.5 设置设置 null 程序清单 2.12(将一个属性设置为 null)2.2.2.6 解决构造方法参数的不确定性解决构造方法参数的不确定性 解决构造方法参数的不确定性有两种方法:?序号:元素有一个 index 属性,可用其来指定构造方法的顺序。程序清单 2.13(使用 index 解决构造方法不确定性)http:/ http:/?类型。程序清单 2.14(使用 type 解决构造方法不确定
45、性)http:/ http:/ 19 学习笔记系列学习比较Spring In Action 学习笔记 2.3 自动装配自动装配 只要设置需要自动装配的中的 autowire 属性即可让 Spring 自动装配。有 4 种自动装配类型:?byName:在容器中寻找和需要自动装配的属性名相同的 Bean(或 ID),如没有找到相符的 Bean,该属性就没有被装配上。?byType:在容器中寻找一个与需要自动装配的属性类型相同的 Bean;如没有找到相符的 Bean,该属性就没有被装配上,如找到超过一个相符的 Bean 抛出异常org.springframework.beans.factory.Un
46、satisfiedDependencyException?constructor:在容器中查找与需要自动装配的 Bean 的构造方法参数一致的一个或多 个Bean。如 存 在 不 确 定Bean或 构 造 方 法,容 器 会 抛 出 异 常org.springframework.beans.factory.UnsatisfiedDependencyException。?autodetect:首先尝试使用 constructor 来自动装配,然后使用 byType 方式。不确定性的处理与 constructor 和 byType 方式一样。2.4 使用使用 Spring 的特殊的特殊 Bean
47、特殊 Bean 的作用:?通过配置后加工 Bean,涉及到 Bean 和 Bean 工厂的生命周期中?从外部配置文件中加载配置信息?改变 Spring 的依赖注入,使其在设置 Bean 属性时,自动将字符串转换成其他类型。?从属性文件中加载文本信息,包括国际化信息?监听并处理由其他 Bean 以及 Spring 容器发布的系统消息?知道其在 Spring 容器的唯一标识 2.4.1 对对 Bean 进行后处理进行后处理 后处理是在 Bean 实例化以及装配完成之后发生的。在 Bean 被创建以及装配之后,BeanPostProcessor 接口提供两次修改 Bean 的机会。程序清单 2.15
48、 BeanPostProcessor.java package org.springframework.beans.factory.config;import org.springframework.beans.BeansException;public interface BeanPostProcessor Object postProcessBeforeInitialization(Object bean,String beanName)throwsBeansException;Object postProcessAfterInitialization(Object bean,String
49、 beanName)throwsBeansException;20 学习笔记系列学习比较Spring In Action 学习笔记 其中 postProcessBeforeInitialization()方法在 Bean 初始化(即调用 afterPropertiesSet()及Bean 指定的 initmethod 方法)之前被调用;同样,postProcessAfterInitialization()方法在初始化之后立即被调用。2.4.1.1 示例示例 程序清单 2.16 Fuddifier.java(后处理 Bean,转换字符串属性)package chapter02.other;imp
50、ort java.lang.reflect.Field;import org.springframework.beans.BeansException;import org.springframework.beans.factory.config.BeanPostProcessor;public class Fuddifier implements BeanPostProcessor /*循环Bean的所有属性,寻找java.lang.String类型的属性。*对于每个String属性,将其传递给fuddify(String)方法,进行转换 */public Object postProces