第1节 初识Spring || 第2章 Spring快速入门
1 Spring与EJB的区别
(1)Spring摈弃了EJB这种重量级的组件,以JavaBean作为组件实现一个轻量级框架。
(2)EJB3.0的出现不会终结Spring。EJB3.0仍仅仅是一个中间件,它不是一个全面的Java EE框架。
2 Spring框架
(1)Spring核心
Spring的核心是IoC容器。
Spring通过Bean工厂来实现基于依赖注入的组件装配。
Spring提供的应用程序上下文(Application Context)封装了许多基本的系统服务,如:访问JNDI,对国际化(i18N)的支持,事件传播,资源装载,电子邮件服务等。
(2)Spring对AOP的支持
Spring的AOP模块提供AOP联盟定义的AOP接口的实现,利用Spring 的AOP支持,可以简化代码逻辑,分离应用程序关注点。
Spring提供的许多底层服务如:对声明事务管理的支持也是基于AOP实现的。
注意:
与其他AOP框架不同的是,Spring的AOP仍然建立在IoC之上,意味着AOP也是以Bean的方式在Spring的IoC容器中装配出来的。
(3)Spring对数据库的访问
Spring DAO定义了一个访问数据库的一致的接口,对JDBC的模版化封装大大简化了JDBC代码编写。
使用Spring DAO的目的之一就是为了隔离应用程序的业务逻辑和数据访问逻辑,从而获得较高的可扩展性。
(4)Spring ORM
对象/关系映射(ORM)模块封装了多种ORM解决方案。但Spring自身并没有提供任何ORM方案,Spring的ORM是为了集成许多流行的ORM框架而设计的,如Hibernate,iBatis等。
(5)Spring的声明式事务
EJB和Spring均有声明式事务管理。但与EJB不同的是,Spring的声明式事务管理是建立在轻量级的AOP基础之上的,却提供了一致的事务模型。
(6)Spring的Web MVC框架
Spring的Web模块提供了一系列针对Web开发的基础功能,如:文件上传,自动绑定参数等。此模块还用于集成其他Web框架,如:Struts。
Spring的MVC框架能无缝集成多种可替代JSP的视图技术,如:Velocity和Freemarker。
3 Spring的设计思想
强调良好的编程实践,如:针对接口的编程,利用依赖注入来解耦,鼓励编写单元测试等。
注意:
默认情况下,Spring的IoC容器管理的对象均是单例(Singleton)。
4 Resin服务器
是一个优秀的免费JavaEE服务器,性能上来讲,Resin是目前速度最快的Servlet容器,远远超过Tomcat。
5 Spring应用程序依赖的两个基本JAR包:
spring.jar和commons-logging.jar
6 Spring应用程序的执行流程:
(1)初始化Spring的Bean工厂
XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource(
“beans.xml”));
(2)通过Bean工厂获得Bean实例
HelloWorld hello = (HelloWorld)factory.getBean(“hello”);
(3)调用Bean工厂获得Bean实例
hello.say();
(4)销毁Spring的Bean工厂
factory.destroySingletons();
7 Spring的配置文件
Spring框架依赖一个XML配置文件来管理和装配应用程序的所有Bean组件。
8 远程调试
Resin服务器和Eclipse都提供对标准远程调试的支持。所谓远程,即跨Java虚拟机。
注意:
如果要远程调试,在启动Resin服务器时,应该加上以下命令行打开远程调试功能:
httpd –Xdebug –Xnoagent –Xrunjdwp:transport=dt_socket,server,server=y,suspend=n,address=12345
9 使用ant来构建项目
Ant是Java构建项目的标准。Ant通过一个XML格式的项目配置文件(通常是build.xml)来构建项目,从而实现从编译到部署的全自动化的项目构建。
一个项目的构建过程:
(1)定义各目录变量
(2)定义ClassPath和外部任务
(3)初始化目录
(4)执行javac编译
(5)生成各种配置文件(如Spring配置文件)
(6)运行JUnit测试
(7)生成项目部署文件
注意:
如果没有使用标准的build.xml作为文件名,需要在命令行中指定XML文件:
ant –buildfile mybuild.xml
10 使用XDoclet自动生成配置文件
XDoclet是开源的自动化生成工具,可用于自动生成Java代码和各类配置文件,包括Spring的XML配置文件。
XDoclet已经提供了对Spring的内置支持。即在Java代码中编写XDoclet注释,然后自动生成Spring的配置文件。
XDoclet只能以Ant的扩展任务形式运行,因此只有使用了Ant作为项目工具构建时,才能用到XDoclet的便捷。
XDoclet的核心是XJavaDoc,这个库负责解析Java代码。
11 Spring2.0的新特性
(1)更容易的配置
(2)对JAP的支持
在JavaEE5.0中,引入了一个新的Java持久化API---JPA(Java Persistent API)。
(3)对JMS的完整支持
JMS(Java Message Service,Java消息服务)是JavaEE应用的标准消息服务,使用JMS能大大减少应用程序的耦合,从而提高应用程序的可维护性和可扩展性。
(4)对Portlet支持
Porlet是一种整合了多个视图的门户页面技术,用户可以自定义页面的布局。
(5)对动态语言的支持(目前Spring2.0支持JRuby,Groovy和BeanShell这3中动态语言)
第3节 使用Spring的IoC容器管理Bean
1 组件
组件是具有一定功能的已编译的类的实现。通常通过接口向外部提供服务,从而能在二进制级别实现复用。而类只能在源代码级别实现复用。
2 依赖注入的3种方式:
(1)构造函数注入
即在构造函数参数中强制注入,如:
public class BookService{
private BookDao bookDao;
public BookServive(BookDao bookDao){
this.bookDao = bookDao;
}}
(2)设置属性注入
通过简单的set方法注入一个符合参数类型的依赖组件。它的优点是XML配置直观,缺点是如果忘记注入某个组件,会抛出NullPointerException。
public class BookService{
private BookDao bookDao;
public BookServive(BookDao bookDao){
this.bookDao = bookDao;
}}
(3)接口注入
在接口中定义需要注入的信息。如:
public interface InjectBookDao{
void injectBookDao(BookDao bookDao);
}
3 Spring提供的IoC容器
Spring的IoC容器是一个高扩展性的无侵入式的框架。所谓无侵入式,是指应用程序的组件无需实现特定的Spring专有接口便可纳入Spring的IoC容器进行管理。
对比Spring的IoC容器,EJB容器是高侵入式的,因为EJB组件必须实现EJB容器的回调接口。
Spring的无侵入式设计的好处:
(1)应用程序组件不依赖于Spring框架也可单独进行测试,大大提高开发效率,降低测试成本。
(2)脱离Spring环境,应用程序也可自行装配组件,避免对Spring框架的过度依赖。
4 Spring的IoC容器
事实上就是实现了BeanFactory接口的可实例化类。
实际上,Spring提供了两种不同的容器:
(1)BeanFactory
(2)扩展的ApplicationContext
BeanFactory仅仅提供最简单的依赖注入支持,而ApplicationContext则扩展BeanFactory,提供更多额外的功能。
(1)BeanFactory使用
Spring的BeanFactory采用的是工厂模式,实现了BeanFactory接口的类负责创建并配置所有的Bean。
启动应用程序à实例化BeanFactory(读取XML配置文件)à从BeanFactory读取Beanà使用Beanà销毁BeanFactory(销毁Bean)à应用程序结束
(2)ApplicationContext使用
ApplicationContext本质上是一个BeanFactory,因为它继承自BeanFactory。与基本的BeanFactory相比,ApplicationContext还提供了国际化支持,事件的发布和通知机制等。
注意:
Spring提供了几种ApplicationContext的实现,包括FileSystemXmlApplicationContext,
ClassPathXmlApplicationContext,XmlWebApplicationContext等。
5 ApplicationContext与BeanFactory的区别:
ApplicationContext初始化Bean和基本的BeanFactory有所不同:
基本的BeanFactory总是延迟加载Bean,直到第一次调用getBean(“beanId”)方法请求Bean的实例时,BeanFactory才会创建这个Bean。因此基本的BeanFactory直到调用getBean(“beanId”)方法获取Bean实例时才会检测配置。
ApplicationContext在自身初始化时就一次性创建了所有Bean,因ApplicationContex在初始化时就能验证XML配置文件的正确性。
ApplicationContext的缺点:由于在启动时需要一次性实例化所有的Bean,如果定义的Bean很多,则启动时间会很长。
6 Spring1.x和Spring2.x的XML配置文件的区别
Spring1.x版本使用DTD来验证XML配置文件:
”1.0” encoding=”UTF-8”?>
“-//SPRING//DTD BEAN//EN” “http://www.springframework.org/dtd/spring-beans.dtd”>
Spring2.0版本既可用DTD验证,也可用Schema验证:
”1.0” encoding=”UTF-8”?>
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
xsi:schemaLocation=”http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd”>
7 Bean的基本的初始化流程
(1)容器根据XML配置文件中的bean的定义实例化一个Bean,并传入必要的构造方法参数
(2)容器根据XML配置文件使用依赖注入设置Bean的属性
(3)如果Bean定义了init-method方法,调用这个方法执行一些初始化工作。
8 Bean的装配
(1)注入基本类型
如:
(2)注入引用类型
如:
(3)注入null
如:
(4)注入List类型和数组类型
如:
//这是一种泛型List
//普通的List
(5)注入Set类型
(6)注入Map类型
//当Map键为String类型
或写成:
//Map键为Bean时
(7)注入Properties类型
Properties类型是由键-值对构成的,Properties的键-值都只能是String,如:
(8)注入Resource资源
Spring提供了一个更强大、更方便的访问底层资源的Resource抽象接口,简化了在Bean中注入各种Resource。
Spring提供了几种Resource的实现:
Ø UrlResource:封装了一个URL对象
Ø ClassPathResource:从ClassPath中获得资源
Ø FileSystemResource:封装了一个File对象,可以访问文件系统资源。
注意:
在Spring的XML配置文件中,注入的Resource资源总是一个字符串来表示的。如:
(9)构造方法注入
在XML配置文件中,需要在
public class ConstructorBean{
public ConstructorBean(int min,int max){
System.out.println(“(int,int)”);
}}
9 Bean的作用域
singleton,prototype,request,session和globalSession
(1)Singleton
这个模式保证一个类在整个应用程序的运行过程有且只有一个实例。
(2)Prototype
如果需要每次返回Bean的新实例,需要Spring容器采用Prototype作用域。如:
(3)其他3种作用域
后3种仅对Web应用程序有效。
10 配置工厂Bean
Spring容器相当于一个复杂的工厂,它负责根据XML配置文件创建并配置Bean。
(1)使用静态工厂
XML片段的配置:
factory-method=”createRandom” />
public class StaticFactoryBean{
public static Integer createRandom(){
return new Integer(new Random().nextInt());
}}
使用这个静态工厂:
Integer rnd = StaticFactoryBean.createRandom();
(2)使用实例工厂
public class InstanceFactory{
private String format = “yy-MM-dd HH:mm:ss”;
public void setFormat(String format){
this.format = format; }
public String createTime(){
return new SimpleDateFormat(format).format(new Date());
}}
factory-method=”createTime” />
注意:
定义实例工厂需要分别定义两个Bean,第一个Bean定义实例工厂本身,第二个Bean定义如何通过实例工厂获取Bean。
(3)实现FactoryBean接口(implements FactoryBean)
注意:
一旦某个Bean实现了FactoryBean接口,这个Bean就不能再被作为普通Bean使用,即:
调用getBean(“IdName”)返回的不是该Bean的实例,而是BeanFactory接口的方法getObject()返回的对象实例。
11 常用的FactoryBean
(1)JndiObjectFactoryBean
用于获取指定的JNDI对象。在JDK5.0中,一共提供4种JNDI实现,分别是LDAP,CORBA,RMI和DNS。
(2)ProxyFactoryBean
用于简化AOP的实现,通过ProxyFactoryBean创建的对象与其代理的对象有相同的接口,但返回给客户端的是经过增强的对象。
(3)TransactionProxyFactoryBean
实现了声明式事务管理,大大简化了事务的配置。TransactionProxyFactoryBean与ProxyFactoryBean类似,不过增加了声明式的事务功能。
(4)LocalSessionFactoryBean
能很方便的生成Hibernate的SessionFactory对象。通过Spring的LocalSessionFactoryBean,甚至不需要Hibernate的配置文件,真正实现了无缝集成。
12 自动装配
使用自动装配,可以减少XML配置文件的部分内容。
Spring容器支持好几种自动装配,分别是byName,byType,constructor和autodetect。
(1)若指定某个Bean采用byName方式的自动装配,为:
//则Spring容器负责自动注入该Bean的所有属性。
(2)byType方式的自动装配,注入与byName类似,不同的是,容器查找的是与属性的设值方法参数类型兼容的Bean。
(3)constructor
容器将找出所有与构造方法的参数类型兼容的Bean,然后确定某个合适的构造方法。若没有符合调用任何构造方法所需的Bean,则容器将抛出UnsatisfiedDependencyException。
(4)autodetect
容器会根据该bean是否有默认的构造方法来决定采用byType方式还是constructor方式。
13 获取容器
如果Bean希望得到Spring容器的引用,可以实现ApplicationContextAware或BeanFactoryAware接口。
14 使用BeanPostProcessor
如果Bean实现了Spring的专有接口BeanPostProcessor,那么在Bean的初始化阶段,Spring容器也提供一种切入机制,允许检查或者修改Bean的属性。具体是实现BeanPostProcessor接口的两种方法:
postProcessBeforeInitialization()和postProcessAfterInitialization()。
15 使用@Required检查依赖注入
Spring2.0提供了一个非常有用的BeanPostProcessor:RequiredAnnotationBeanPostProcessor,它负责检查一个Bean的某个被标记为@Required注解的属性是否被注入了。
好处:
对于检查一些必须要注入的属性来说非常有用,它避免了在运行期由于忘记注入某个属性而导致的NullPointerException。
16 使用BeanFactoryPostProcessor
BeanPostProcessor使我们有能力在Spring容器对Bean初始化前后对所有的Bean进行检查或修改。而BeanFactoryPostProcessor允许我们在Spring容器对所有的Bean初始化之前对Bean的定义做一些修改。
17 定制属性编辑器
标准的java.beans.PropertyEditor接口定义了一个String转化为任意类型的setAsText()方法。
Spring已经内置了许多PropertyEditor的实现,并且某些PropertyEditor已经由BeanWrapperImpl自动注册到容器中。常用的PropertyEditor如下:
Ø ByteArrayPropertyEditor:Stringàbyte[]
Ø CharArrayPropertyEditor:Stringàchar[]
Ø ClassEditor:Stringàjava.util.Date类型
Ø CustomDateEditor:Stringàjava.util.Date
Ø ResourceEditor:Stringàorg.springframework.core.io.Resource类
Ø InputStreamEditor:将http://,“ftp://“等合法地址转换为java.io.InputStream
Ø LocaleEditor:StringàLocale对象
Ø StringTrimmerEditor:对String实现trim()操作,并能将一个空字符串转换为null
Ø URLEditor:StringàURL对象
18 发布和接收事件
Spring的ApplicationContext容器可以支持发布和接收事件通知,其实现原理是基于标准的Observer模式。
Spring自身定义了3种事件:
ContextClosedEvent、ContextRefreshEvent和RequestHandledEvent
如果一个Bean需要向其他Bean发布事件通知,只需要调用ApplicationContext的publishEvent()方法。但要注意:Spring容器的事件发布机制是同步的,即调用ApplicationContext.publishEvent()方法时,直到所有的Bean都处理完该事件,方法才会返回。
19 分拆配置文件
20 local的使用
Spring提供local来明确指定注入的Bean必须在当前XML配置文件定义,如:
21 容器的继承
实现具有继承功能的容器必须实现HierachicalBeanFactory接口。
对于基本的BeanFactory容器,只有DefaultListableBeanFactory和XmlBeanFactory具有HierachicalBeanFactory接口。
对于ApplicationContext容器,由于ApplicationContext接口本身具有HierachicalBeanFactory接口,因此所有的ApplicationContext的实现都具有继承功能。
22 使用XDoclet自动生成配置文件
XDoclet是一个模版工具,它扫描源代码并提取特殊的注释,再根据模版自动生成配置文件。
(1)定义Bean
/**
* @spring.bean id=”basicBean”
*/
(2)注入属性
/**
* @spring.property value=”100”
*/
/**
* @spring.property ref=”basicBean”
*/
(3)使用Merge功能
当应用程序使用的Bean不是自己开发的,而是现有的组件,如JDK或Spring自身的类,此时就无法通过编写特定的注释来自动生成配置文件。
解决:
手动编写一个固定的XML配置片段,这个固定配置文件的文件名必须是spring-beans.xml,在Ant脚本中指定mergeDir后,XDoclet就在这个目录下查找spring-beans.xml,如果找到,就将其嵌入到最终的XML配置文件中。
如:
假定我们定义了一个java.util.Date对象,编写spring-beans.xml如下:
注意:该文件没有常规的XML头,只能定义
(4)扩展XDoclet
XDcolet模版极具扩展性,我们可以手动扩展XDoclet未提供Spring配置文件的scope,constructor-arg中的type等属性。如:
/**
* @spring.bean id=”basicBean” scope=”prototype”
*/
第4节 使用Spring AOP
1 AOP的实现原理
在调用目标对象的某一业务方法时,能够拦截该方法的调用,从而将切面织入到应用程序的流程中。
在Java平台上,对于AOP模块的织入,有3种方式:
(1)编译期
切面在编译期织入,这类似于代码的自动生成技术。需要定义新的语法关键字并由特殊的编译器来实现。(如:AspectJ)
(2)类装载器
在目标类被装载到Java虚拟机时,由一个特殊的类装载器对目标类的字节码进行增强。
(3)运行期
目标对象和切面都是标准的Java类,通过Java虚拟机的动态代理功能或CGLIB(Java的动态代理)实现在运行期的动态织入。(如:Spring AOP)
注意:
AOP并非一种全新的技术,仅仅是使用了更简单,更优雅的设计来实现传统的Proxy,Decorator等模式。
2 不同的AOP实现
(1)AspectJ的AOP
AspectJ是Java平台上最早的AOP实现。AspectJ扩展了Java语法,定义了额外的AOP语法关键字,需要一个专门的编译器来生成Java字节码。
AspectJ是目前最完善的AOP解决方案,提供了AOP技术的所有特征。
(2)Spring的AOP实现
Spring的AOP使用纯Java实现,不需要特殊的语法和专门的编译过程,也不会更改类装载器,因此适合于Web应用程序。
Spring AOP仅支持对方法的增强,这和AspectJ不同。
Spring AOP的目标是让AOP技术在Spring的IoC容器中完美集成,因此Spring AOP通常与Spring的IoC容器一起使用来解决企业开发中的常用问题。
(3)利用动态代理实现AOP
动态代理无需静态编译的具体类,就允许应用程序在运行期动态创建一个实现了一个或多个的接口的代理对象,并设置一个拦截器,调用代理对象的任何方法都会被拦截器截获,从而可以动态实现一个对象。
(区别于传统的面向对象设计,必须首先有一个实现了某个接口的具体类才能转型为接口类型)
注意:
Ø 与继承机制不同的是,动态代理并没有具体地实现某个接口,它仅仅在运行期被创建,然后动态指定了其实现的接口。
Ø 动态代理的一个重要作用是减少暴露给客户端的接口。
Ø 动态代理机制的核心是实现了InvocationHandler接口的拦截器
3 Spring框架提供了JdkDynamicProxyFactory来使用JDK动态代理。
使用Java虚拟机的动态代理来实现AOP时的一个限制是:只能对接口代理,无法对一个实际类代理。
4 Spring AOP基础
(1)术语
Ø Aspect:切面
切面就是一个横跨多个核心逻辑的功能,或者称之为系统关注点,如:日志记录,事务管理,安全检查等。
Ø Joinpoint:连接点
连接点就是定义在应用程序的何处插入切面的执行,如在一个方法调用时,或者在访问一个字段时,或特定的异常抛出时。
Ø Pointcut:切入点
切入点就是一组连接点的集合。
Ø Advice:增强
在特定连接点上执行的动作,执行这个动作就相当于对原始对象的功能做了增强。
Ø Introduction:引介
为已有的Java对象动态地增加新的接口。
Ø Weaving:织入
就是将切面整合到程序的执行流程中。
Ø Interceptor:拦截器
拦截器是如何实现增强的一种方式
Ø Target Object:目标对象
目标对象是真正执行核心逻辑的对象。
Ø AOP Proxy:AOP代理
就是客户端持有的引用。
5 在Spring中装配AOP
Spring提倡使用依赖注入来装配Bean,对于AOP也一样。
Spring提供了ProxyFactoryBean来实现AOP的装配。
6 Advice
在Java中,Advice本质上就是一个拦截器,可以拦截方法调用,然后执行自己的一段代码来增强应用程序的逻辑。但Advice并不能决定拦截哪些方法,这由Pointcut决定的。
常用的Advice:
(1)MethodBeforeAdvice:在方法执行开始前增强
(2)AfterReturningAdvice:在一个方法执行完毕后增强
(3)ThrowsAdvice:在方法执行期抛出异常时增强
(4)MethodInterceptor:通常称为“环绕通知”,可实现前面3中Advice的所有功能,是在方法执行前后增强(可以控制整个方法的执行与否,甚至可以修改方法的返回值)
注意:
具体编写的Advive类实现以上4种接口之一即可完成Advice的编写。
7 使用ProxyFactoryBean装配AOP
编写bean.xml文件,将其在SpringIoC容器装配起来。如:
—注入目标对象-->
8 编写Advisor
Spring定义了Advisor的概念,它包含了一个Advice,并且定义了如何将Advice织入到目标对象。
最重要的Advisor是PointcutAdvisor,PointcutAdvisor包含了一个Advice和一个Pointcut。
几个重要的Pointcut实现:
(1)NameMatchMethodPointcut
它提供了两个非常方便的方法来定义方法切入点:
setMappedName(String mappedName)
setMappedName(String[] mappedNames)
(2)AbstractRegexMethodPointcut
根据正则表达式来计算切入点。AbstractRegexMethodPointcut对应的Advisor是RegexpMethodPointcutAdvisor,RegexpMethodPointcutAdvisor会根据当前JDK版本决定使用JdkRegexpMethodPointcut(JDK1.4或更高版本)还是Perl5RegexpMethodPointcut(JDK1.4之前版本),通过pattern属性指定切入点:
9 AOP代理对象
AOP代理对象实际上包含了一个由Advisor和Advice组成的拦截链。
10 使用自动代理
Spring的自动代理的两个功能:
(1)自动为多个目标Bean实现AOP代理
(2)避免客户端直接访问目标Bean。
Spring的自动代理功能实际上是由BeanPostProcessor实现的。在容器载入XML配置文件后,具有自动代理功能的BeanPostProcessor就可以修改Bean的定义,将所有需要实现代理的目标Bean全部修改为代理Bean,而id不变。
这样容器就不直接持有目标Bean的引用,而仅持有代理Bean的引用,客户端也就无从访问目标Bean了。
11 Spring提供的几种常用的实现自动代理的BeanPostProcessor
(1)BeanNameAutoProxyCreator
根据Bean的id或name属性来查找目标Bean并自动为其代理。
(2)DefaultAdvisorAutoProxyCreator
根据当前的Advisor决定每个Bean是否可以被代理,若可以,就自动创建代理,并自动织入所有可用的Advisor。
(3)AspectJInvocationContextExposingAdvisorProxyCreator
根据AspectJ的注解来决定是否为一个Bean创建代理
12 使用引介
引介(Introduction)是一种特殊类型的拦截器。与普通拦截器不同的是,引介不能作用于任何切入点,引介只能作用于类,而非方法级别。
或者说,引介的作用就是要给一个已有的类动态增加接口。
13 在运行期改变AOP代理
在创建了AOP代理后,还可以将AOP代理转型为org.springframework.aop.framework.Advised接口,因为任何AOP代理都一定实现这个接口。然后调用Advised接口的方法在运行期动态修改一个AOP代理,如:添加或删除Advice或Advisor。
扩展:
如果要防止在运行期修改AOP代理对象,可以设置frozen属性为true。
一旦设置ProxyFactoryBean的frozen属性为true,任何增加或者移除通知的修改都会导致一个AopConfigException异常,这样就能确保AOP代理对象一旦被创建就无法更改。
14 使用@AspectJ实现AOP
使用AspectJ注解实现AOP的最大的优点是将多个Advisor集中到一个类中,并且配置极为简单。
注意:
Spring AOP在运行时仍是纯Java的实现,并不依赖于AspectJ的编译器和织入器。
(1)声明Aspect
标注为@Aspect的类表示这是一个切面类。一个切面类可以包含多个Advice和Pointcut,每个Advice和Pointcut都是以一个独立的方法加上特定的注解来表示。
(2)声明Advice
Advice可以通过定义一个方法并加上特定的注解来指定Pointcut。
注意:
Spring支持以下几种主要的AspectJ风格的Pointcut:
Ø execution:匹配方法执行的切入点,这也是Spring中最常用的切入点定义方式
格式:execution(修饰符?返回类型 声明类型? 方法名称(参数类型) 异常类型?)
Ø within:匹配特定类型的切入点
Ø this:匹配特定实例的切入点
此外,AspectJ还支持诸如call,initialization,if等切入点类型。不过在Spring中,不能使用这些不被支持的定义。
(3)声明Pointcut
用@Pointcut注解将类中的几个Advice使用的Pointcut表达式提取出来。
第5节 Spring数据访问策略
1 DataSource(数据源)
DataSource可以看作获取Connection的工厂,它管理并维护一个Connection对象池,能支持大量并发的数据库连接请求。
DataSource可以管理并缓存Connection,因此关闭连接并不意味着实际的数据库连接被关闭了,可能是Connection标记为“空闲”状态。这样可避免反复创建和关闭数据库连接所带来的开销,大大提高运行效率。
注意:
DataSource的更多的好处:
(1)在DriverManager中必须将数据库的配置信息硬编码到代码中,这种方式不够安全且缺乏灵活性。而DataSource的配置可以通过JavaEE服务器的配置完成,应用程序无需管理这些配置信息。
(2)应用程序无需知道底层数据库
(3)DataSource可以限制应用程序的连接数目,避免服务器由于连接数目过多导致负载过重甚至崩溃
2 Spring封装的数据库访问异常
异常 |
意义 |
DataAccessException |
Spring DAO框架的异常体系根类,该类extends NestedRuntimeException |
ConcurrencyFailureException |
在多个并发操作时,无法乐观锁定或者获得数据库锁 |
DataAccessResourceFailureException |
访问数据失败 |
DataRetrievalFailureException |
无法获取指定的数据 |
InvalidDataAccessResourceUsageException |
无效的数据访问方法(如语法错误的SQL语句) |
PermissionDeniedDataAccessException |
没有数据访问权限 |
UncategorizedDataAccessException |
无法归类的异常(如:特定数据库的错误代码无法被Spring识别) |
3 DAO模式
DAO(Data Access Object)已经成为JavaEE系统的标准的数据访问模式。通过DAO接口,应用程序将底层的数据访问和上层的业务逻辑代码完全分离,使得各层保持相对独立,便于扩展和移植。
Spring DAO模版的好处:
(1)Spring框架已经在DAO模版中封装了相当多的功能,例如:
对于JdbcDaoSupport类,只需要注入DataSource,JdbcDaoSupport就自动为我们提供JdbcTemplate的实例,并且无需关心Connection的获取和释放。
(2)Spring DAO模版提供了一致的数据访问模式
采用Spring提供的一致的DAO模式,则向上层屏蔽了底层数据访问的细节,上层持有的通常是DAO接口,因此无需关心DAO的具体实现究竟采用何种数据访问策略。
注意:
通过DAO接口的这个抽象层,所有的数据库操作对于应用程序上层而言是完全不可见的,应用程序上层也不需要关心访问数据库的细节。
通常,需要对DAO接口定义GRUD操作,即:Create,Retrieve,Update,Delete,表示数据库的创建、查询、更新和删除操作。
4 JdbcTemplate
Spring提供的一个强有力的模版类JdbcTemplate,来简化JDBC操作,并且DataSource和JdbcTemplate全部都可以以Bean的形式定义在XML配置文件中,发挥依赖注入的威力。
JdbcTemplate的创建需要一个DataSource接口,由于DataSource一般是由管理员在服务器上部署,对于Web应用程序,只需要通过JNDI获得DataSource的引用即可
注意:
Spring提供了一个简单的DriverManagerDataSource,可以直接以Bean的形式定义在XML配置文件中:
…
定义JdbcTemplate
将JdbcTemplate对象注入到自定义的DAO对象中,如MyBookDao
public class MyBookDao implements BookDao{
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate){
this.jdbcTemplate = jdbcTemplate;
}}
如果自定义的DAO是直接从JdbcDaoSupport派生的,就只需要注入一个DataSource,JdbcTemplate对象会自动生成。可以通过getJdbcTemplate()方法获得JdbcTemplate对象的引用。
如果存在多个DAO对象,则不必为每个DAO对象都创建一个JdbcTemplate对象,因为JdbcTemplate对象是线程安全的,可以被所有的DAO对象共享。
5 JdbcTemplate提供的简化JDBC操作的主要方法:
(1)List query(String sql,Object[]args,RowMapper rowMapper)
args是传递给SQL语句的参数,而rowMapper负责将每一行记录转化为一个Java对象并存放在List中。返回的List是一个包含了Java对象的结果集,而不是原始的ResultSet结果集。
6 Spring中集成Hibernate
Hibernate提供了一系列复杂的功能来简化数据库操作:
(1)延迟加载
(2)主动抓取
(3)缓存
(4)级联操作
Hibernate是对JDBC的一层较薄的封装,Hibernate中的主要类与JDBC的主要类的对应关系:
Hibernate |
功能 |
对应的JDBC类 |
SessionFactory |
封装一个数据源,是线程安全的 |
DataSource |
Session |
封装一个较短的数据访问回话,非线程安全 |
Connection |
Query |
封装一个数据查询,非线程安全 |
PreparedStatement |
Transaction |
封装一个事务,非线程安全 |
JDBC Transaction或JTA |
Hibernate与JDBC的联系:
SessionFactory sf=config.buildSessionFactory();
Session session = sf.openSession();
Transaction tx = session.beginTransaction();
Query query = session.createQuery(“select o from Book as o”);
List
tx.commit();
session.close(); |
DataSource ds = jndiLookup(“ds”);
Connection conn = ds.getConnection();
conn.setAutoCommit(false);
PreparedStatement ps =conn.PrepareStatement(“select * from Book”);
ResultSet rs = ps.executeQuery(); while(rs.next()){…} conn.commit();
conn.close(); |
7 配置Hibernate
在Spring中使用Hibernate的两种基本的方式:
(1)完全不用Spring对Hibernate的封装,此时需要开发者自己管理Hibernate的资源,如SessionFactory,Session,Query等。
(2)Spring提供以下封装Hibernate的Bean,可以方便地实现Hibernate的
操作,如:
LocalSessionFactoryBean |
封装Hibernate的SessionFactory |
AnnotationSessionFactoryBean |
支持Annotation配置的SessionFactory |
HibernateTemplate |
操作Hibernate的模版类,是操作Hibernate最重要的类 |
HibernateDaoSupport |
实现DAO支持的类 |
HibernateTemplate提供了一系列方法来实现数据库的GRUD操作:
(1)Serializable save(Object entity)
保存一个对象到数据库,并返回其主键
(2)Object get(Class entityClass,Serializable id)
通过主键获取一个指定类型的对象,若对应的数据库记录不存在,则返回null。
(3)Object load(String entityName,Serializable id)
和get类似,不同在于,如果对应的数据库记录不存在,将抛出ObjectRetrievalFailureException异常
(4)void update(Object entity)
更新一个已存在的对象
(5)void delete(Object entity)
删除一个已存在的对象
8 Hibernate注解在Hibernate Template实现GRUD操作时的使用
@Entity注解:必须在ClassPath定义处,表示这个class将作为数据库表的映射,name属性指定了数据库的表名。
@Id注解表示类的id属性将被映射为数据表的主键,没有任何标记的getXxx()方法表示将数据库表中的对应字段映射到该属性上。
@Column注解定义将哪个字段映射到该属性上。
此外,所有的注解(除@Entity)都必须标记在get方法前,注解在set方法前是无效的。注解被定义在ejb-persistence.jar包中,需引入javax.persistence.*包。
9 使用Hibernate注解验证数据
常见的Hibernate验证注解:
注解 |
适用类型 |
说明 |
示例 |
@Pattern |
String |
通过正则表达式来验证字符串 |
@Pattern(regex=”[a-z]{8}”) |
@Length |
String |
验证字符串的长度 |
@Length(min=3,max=20) |
|
String |
验证一个E-mail地址是否有效 |
|
@Range |
long |
验证一个整型是否在指定范围内 |
@Range(min=0,max=100) |
@Min |
long |
验证一个整型必须不小于指定值 |
@Min(value=10) |
@Max |
long |
验证一个整型必须不大于指定值 |
@Max(value=20) |
@Size |
集合或数组 |
验证集合或数组的大小是否在指定范围内 |
@Size(min=1,max=255) |
10 集成IBatis
IBatis的配置:
需要为每一个实体定义其各自的映射和所有用到的SQL语句,然后在一个根配置文件中包含它们。(将ibatis-common-2.jar和ibatis-sqlmap-2.jar放入ClassPath中)
11 集成JDO
JDO即Java Data Object,是JavaEE平台的另一个持久性规范。JDO作为一种轻量级的持久化规范可用于任何Java环境。
JPOX是JDO2.0标准的参考实现,使用JPOX之前,需要以下jar文件:
(1)jdo2-api-2.0.jar
是JDO2.0标准的接口
(2)jpox-1.1.5.jar和jpox-enhancer-1.1.5.jar
jpox-1.1.5.jar是JPOX对JDO2.0的实现,jpox-enhancer-1.1.5.jar用于对持久化对象进行增强
(3)log4j-1.2.x.jar
是log4j是JPOX运行时必须的日志库
(4)bcel-5.2.jar
12 集成JPA
JPA即Java Persistence API(Java持久化接口),是JavaEE 5.0中引入的新的标准的持久化API。JPA是EJB3.0的一部分,而EJB3.0又是JavaEE 5.0规范的一部分。
JPA的设计目的:
主要是为了EJB3.0提供持久化支持,但是JPA并不依赖EJB。从一开始,JPA就被设计成可以在EJB容器之外独立使用。实际上,JPA也可以在J2SE的环境中启动。
JPA定义了两个重要的规范:
(1)通过JPA注解,使得实体的映射配置可以通过注解来完成,而不必编写复杂的XML配置文件。(Hibernate3.2可以甚至可以通过Hibernate Annotation库实现JPA注解配置映射,对于JDK1.4或更低版本,JPA也支持传统的XML配置文件)
(2)JPA定义了一个统一的数据访问接口,包括EntityManagerFactory、EntityManager等。
注意:
Spring对JPA的支持采用Adapter模式,并且提供了JpaDaoSupport和JpaTemplate支持类,使JPA和其他持久化机制(如Hibernate,JDO)拥有一致的编程模型。
第6节 Spring事务管理
1 事务
是数据库系统为保证数据操作的完整性和一致性引入的。所谓事务,也就是将一系列的数据库操作作为一个整体来执行。
数据库事务的特性:
ACID(原子,一致,隔离,持久性)
常用的关系数据库系统是依赖日志和锁机制来保证事务具有ACID特性。
2 事务的隔离级别
数据库支持的4种隔离级别:
(1)ISOLATION_READ_UNCOMMITTED:允许读到其他事务没有提交的数据,可能会造成脏读
(2)ISOLATION_READ_COMMITTED:只允许读已经提交的数据,避免脏读
(3)ISOLATION_REPEATABLE_READ
(4)ISOLATION_SERIALIZABLE:最严格的隔离级别,操作同一数据的并发事务只能以串行方式执行,即第一个事务结束后才能执行下一个事务。
3 JTA事务
JTA即Java Transaction API,即Java事务API,是JavaEE标准的一部分。JTA标准是建立在JTS(Java Transaction Service,Java事务服务)标准之上。
JTS是一个底层的API,供应用服务器厂商提供,而JTA是高层接口,供开发者使用。
JTA使用事务管理器(Transaction Manager)来管理分布式事务。一个JTA事务涉及一个事务管理器和多个资源管理器。
4 Spring的事务模型
Spring框架的事务处理功能,其核心是PlatformTransactionManager抽象接口。
Spring将所有的事务管理都抽象为PlatformTransactionManager,TransactionStatus和TransactionDefinition这3个接口,而无论其底层关联的具体的事务究竟是JDBC事务,JTA事务,还是ORM框架自定义的事务。
Spring提供了以下事务传播行为,这和EJB声明式事务的传播行为是一致的:
(1)PROPAGATION_REQUIRED:必须在事务内执行,是Spring的默认事务传播行为
(2)PROPAGATION_SUPPORT:支持当前事务
(3)PROPAFATION_MANDATORY:必须在当前事务内执行
(4)PROPAGATION_REQUIRES_NEW:总是新建一个事务,把当前事务挂起直到新事务执行完毕
(5)PROPAGATION_NOT_SUPPORTED:不能在事务环境下执行,如果当前存在事务,就把当前事务挂起。
(6)PROPAGATION_NEVER:不能在事务环境下执行,如果当前存在事务,就抛出异常
(7)PROPAGATION_NESTED:必须在事务内执行