




已阅读5页,还剩27页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第7章 在Spring中管理事务 7.1 知识点:AOP简介 7.2 开发步骤 7.1 知识点:AOP简介 7.1.1 从代理机制初探AOP 从一个简单常见的例子开始。这个例子当中含有日志动作,程序中经常需要为某些 动作或事件作下记录,以便随时检查程序运行过程和排除错误的信息。来看一个简单的 例子,当需要在执行某些方法时留下日志信息,直觉的,可能会这样写。 import java.util.logging.*; public class HelloSpeaker pirvate Logger logger=Logger.getLogger(this.getClass().getName(); public void hello(String name) logger.log(Level.INFO,”hello method starts”);/方法执行开始时留下日志 Sytem.out.println(”hello, ”+name); /程序的主要功能 Logger.log(Level.INFO, ”hello method ends”);/方法执行完毕时留下日志 7.1.1 从代理机制初探AOP 在HelloSpeaker类中,当执行hello()方法时,程序员希望开始执行该方法与执行完毕 时都会留下日志,最简单的做法是用上面的程序设计,在方法执行的前后加上日志动作。 然而对于HelloSpeaker来说,日志的这种动作并不属于HelloSpeaker逻辑,这使得 HelloSpeaker增加了额外的职责。 如果程序中这种日志动作到处都有需求,以上的写法势必造成程序员必须到处撰写 这些日志动作的代码。这将使得维护日志代码的困难加大。如果需要的服务不只是日志动 作,有一些非类本身职责的相关动作也混入到类中,比如权限检查,事务管理等等,会使 得类的负担加重,甚至混淆类本身的职责。 另一方面,使用以上的写法,如果有一天不再需要日志(或权限检查,交易管理等 )的服务,将需要修改所有留下日志动作的程序,无法简单地就将这些相关服务从现有的 程序中移除。 可以使用代理(Proxy)机制来解决这个问题,有两种代理方式:静态代理(static proxy)和动态代理(dynamic proxy)。 在静态代理的实现中,代理类与被代理的类必须实现同一个接口,在代理类中可以 实现记录等相关服务,并在需要的时候再呼叫被代理类。这样被代理类中就可以仅仅保留 业务相关的职责了。 7.1.1 从代理机制初探AOP 举个简单的例子,首先定义一个IHello接口: IHello.java代码如下: public interface Ihello public void hello(String name); 然后让实现业务逻辑的HelloSpeaker类别实现Ihello接口,HelloSpeaker.java代码 如下: public class HelloSpeaker implements Ihello public void hello(String name) Sytem.out.println(”hello,”+name); 可以看到,在HelloSpeaker类中没有任何日志的代码插入其中,日志服务的实现 将被放到代理类中,代理类同样要实现IHello接口: 7.1.1 从代理机制初探AOP HelloProxy.java代码如下: public class HelloProxy implements Ihello private Logger logger=Logger.getLogger(this.getClass().getName(); private Ihello helloObject; public HelloProxy(Ihello helloObject) this.helloObject=helloObject; public void hello(String name) log(”hello method starts”);/日志服务 helloObject.hello(name);/执行业务逻辑 log(”hello method ends”);/日志服务 private void log(String ms) logger.log(Level.INFO,msg); 7.1.1 从代理机制初探AOP 在HelloProxy类的hello()方法中,真正实现业务逻辑前后可以安排记录服务,可以 实际撰写一个测试程序来看看如何使用代理类。 public class ProxyDemo public static void main(String args) IHello proxy=new HelloProxy(new HelloSpeaker(); proxy.hello(”Justin”); 这是静态代理的基本示例,但是可以看到,代理类的一个接口只能服务于一种类型 的类,而且如果要代理的方法很多,势必要为每个方法进行代理,静态代理在程序规模稍 大时就必定无法胜任。 7.1.2 动态代理 在JDK1.3之后加入了可协助开发动态代理功能的API等相关类别,不需要为特定类 和方法编写特定的代理类,使用动态代理,可以使得一个处理者(Handler)为各个类 服务。 要实现动态代理,同样需要定义所要代理的接口: IHello.java代码如下: public interface IHello public void hello(String name); 然后让实现业务逻辑的HelloSpeaker类别要实现IHello接口,如 HelloSpeaker.java代码如下: public class HelloSpeaker implements IHello public void hello(String name) System.out.println(”Hello, ”+name); 7.1.2 动态代理 写一个测试程序,如果要使用LogHandler的bind()方法来绑定被代理类。 ProxyDemo.java代码如下: public class ProxyDemo public static void main(String args) LogHandler logHandler=new LogHandler(); IHello helloProxy=(IHello)logHandler.bind(new HelloSpeaker(); helloProxy.hello(”Justin”); HelloSpeaker本身的职责是显示文字,却必须插入日志动作,这使得HelloSpeaker 的职责加重。在AOP的是术语中,日志的程序代码横切(cross-cutting)到HelloSpeaker的 程序执行流程中,日志这样的动作在AOP中被称为横切关注点(cross-cutting concern)。 使用代理类将记录与业务逻辑无关的动作提取出来,设计为一个服务类,如同前 面的范例HelloProxy或者LogHandler,这样的类称之为切面(aspect)。 AOP中的aspect所指的可以是像日志等这类的动作或服务,将这些动作(cross- cutting concern)设计为通用,不介入特定业务类的一个职责清楚的Aspect类,这就是所 谓的Aspect-oriented programming,AOP。 7.1.3 AOP术语与概念 介绍AOP术语和概念。 Cross-cutting concer 在DynamicProxyDemo的例子中,记录的动作原先被横切(Cross-cutting)到 HelloSpeaker本身所负责的业务流程中。另外类似于日志这类的动作,如安全检查、事务 等服务,在一个应用程序中常被安排到各个类的处理流程之中。这些动作在AOP的术语中 被称为Cross-cutting conncers。 如图7-1所示,原来的业务流程是很单纯的。 图7-1 原来的业务流程 7.1.3 AOP术语与概念 Cross-cutting concerns如果直接写在负责某业务的类的流程中,使得维护程序的成 本增加。如果以后要把类的记录功能修改或者移除这些服务,则必须修改所有撰写曾记录 服务的程序,然后重新编译。另一方面,Cross-cutting concerns混杂在业务逻辑之中, 使得业务类本身的逻辑或者程序的撰写更为复杂。 如同7-2所示,为了要加入日志与安全检查等服务,类的程序代码中被硬生生地写如 了相关的Logging、Security程序片段。 图7-2 加入各种服务的业务流程 7.1.3 AOP术语与概念 Aspect 将散落在各个业务类中的Cross-cutting concerns收集起来,设计各个独立可重用的 类,这种类称之为Aspect。例如在动态代理中将日志的动作设计为一个LogHandler类 ,LogHandler类在AOP术语中就是Aspect的一个具体实例。在需要该服务的时候,缝 合到应用程序中;不需要服务的时候,也可以马上从应用程序中脱离。应用程序中的可 重用组件不用做任何的修改,例如在动态代理中的HelloSpeaker所代表的角色就是应用 程序中可重用的组件,在它需要日志服务时并不用修改本身的程序代码。 另一方面,对于应用程序中可重用的组件来说,以AOP的设计方式,它不用知道 处理提供服务的类的存在。即与服务相关的API不会出现在可重用的应用组件中,因而 可提高这些组件的重用性,可以将这些组件应用到其他的应用程序中,而不会因为目前 加入了某个服务或与目前的应用框架发生耦合。 不同的AOP框架对AOP概念有不同的实现方式,主要的差别在于所提供的 Aspects的丰富程度,以及它们如何被缝合(Weave)到应用程序中。 7.1.4 通知Advice Spring提供了5种通知(Advice)类型:Interception Around、Before、After Returning、Throw 和Introduction。它们分别在以下情况被调用: Interception Around Advice:在目标对象的方法执行前后被调用。 Before Advice:在目标对象的方法执行前被调用。 After Returning Advice:在目标对象的方法执行后被调用 Throw Advice:在目标对象的方法抛出异常时被调用。 Introduction Advice:一种特殊类型的拦截通知,只有在目标对象的方法调用完毕 后执行。 这里,用过前置通知Before Advice来说明。 Before Advice会在目标对象的方法执行之前被呼叫。如同在便利店里,在客户购买 东西之前,老板要给它们一个热情的招呼。为了实现这一点,需要扩展 MethodBeforAdvice接口。这个接口提供了获取目标方法、参数以及目标对象。 public interface MethodBeforeAdvice void before(Method method, Object args, Object target) throws Throwable 7.1.4 通知Advice 用实例来示范如何使用Before Advice。首先要定义目标对象必须实现的接口: IHello.java代码如下: public inter IHello public void hello(String name); 接着定义一个HelloSpeaker了,让它实现IHello接口: HelloSpeaker.java代码如下: public class HelloSpeaker implements IHello public void hello(String name) System.out.println(“Hello,”+name); 在对HelloSpeader不进行任何修改的情况下,想要在hello()方法执行之前,可以记录 一些信息,有一个组件,但是没有源代码,但是想对它增加一些日志的服务。可以先实现 MethodBeoforeAdvice接口,例如: 7.1.4 通知Advice LogBeforeAdvice.java代码如下: import java.lang.reflect.Method; import java.util.logging.Level; import java.util.logging.Logger; import org.springframework.aop.MethodBeforeAdvice; public class LogBeforeAdvice implements MethodBeforAdvice private Logger logger=Logger.getLogger(this.getClass().getName(); public void before(Mehod method,Object args,Object target) throws Throwable logger.log(Levl.INFO, ”method starts”+method); 7.1.4 通知Advice 在before()方法中,加入了一些记录信息的程序代码。LogBeforeAdvice类被设计为 一个独立的服务,接着只要在定义文档中如下定义: beans-config.xml代码如下: IHello logBeforeAdvice 7.1.4 通知Advice 注意,除了建立Advice和Target实例之外,还使用了 org.springframework.aop.framework.ProxyBean。这个类会被BeanFactory或者 ApplicationContext用来建立代理对象。需要在“proxyInterfaces”属性中告诉代理可运行的 界面,在“target”上告诉Target对象,在“interceptorNames”上告诉要应用的Advice实例, 在不指定目标方法的时候,Before Advice会被缝合(Weave)到界面上多处有定义的方法之 前。 写一个程序测试一下Before Advice的运作: SpringAOPDemo.java代码如下: import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; public class SpringAOPDemo public static void main(String args) ApplicationContext context=new FileSystemXmlApplicationContext(“bean- config.xml”); IHello helloProxy=(IHello)context.getBean(“helloProxy”); helloProxy.hello(“Justin”); HelloSpeaker与LogBeforeAdvice是两个独立的类。对于HelloSpeaker来说,它不用 知道LogBeforeAdvice的存在;而LogBeforeAdvice也可以运行到其他类之上。 HelloSpeaker与LogBefore都可以重复使用。 7.1.5 切入点PointCut Pointcut定义了通知Advice应用的时机。从一个实例开始,介绍如何使用Spring提供 的org.springframework.aop.support.NameMatchMethodPointcutAdvisor。可以指定 Advice所要应用的目标上的方法名称,或者是用*来指定。例如,hello*表示调用代理对 象上以hello作为开头的方法名称时,都会应用指定的Advices。 IHello.java代码如下: public interface IHello public void helloNewbie(String name); public void helloMaster(String name); HelloSpeaker类实现IHello接口。HelloSpeaker.java代码如下: public class HelloSpeaker public void helloNewbie(String name) System.out.println(”Hello, ”+name+”newbie! ”); public void helloMaster(String name) System.out.println(”Hello, ”+name+”master! ”); 7.1.5 切入点PointCut 编写一个简单的Advice,这里使用Before Adviece中LogBeforeAdvice。 定义Bean文档,使用NameMatchMethodPointcutAdvisor将Pointcut与Advice结合在 一起: bean-config.xml代码如下: hello* 7.1.5 切入点PointCut IHello helloAdvisor 7.1.5 切入点PointCut 在NameMatchMethodPointcutAdvosor的“mappedName”属性上,由于指定了“hello*” ,所以当调用helloNewbie()或者helloMaster()方法时,由于方法名称的开头符合“hello”, 就会应用logBeforeAdvice的服务逻辑,可以写一下程序来测试。 SpringAOPDemo.java代码如下: import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; public class SpringAOPDemo public static void main(String args) ApplicationContext context=new FileSystemXmlApplicationContext(“bean-config.xml”); IHello helloProxy=(IHello)context.getBean(“helloProxy”); helloProxy.helloNewbie(“Justin”); helloProxy.helloMaster(“Tom”); 7.1.5 切入点PointCut 在Spring中使用PointcutAdvisor把Pointcut与Advice结合为一个对象。Spring中大部 分内建的Pointcut都有对应的PointAdvisor。 org.springframework.aop.support.NameMatchMethodPointcutAdvisor,这是最简单的 PointAdvisor,它是Spring中静态的Pointcut实例。使用 org.springframework.aop.support.RegexpMethodPointcut可以实现静态切入点, RegexpMethodPointcut是一个通用的正则表达式切入点,它是通过Jakarta ORO来实现 的。 静态切入点只限于给定的方法和目标类,而不考虑方法的参数。动态切入点与静态 切入点的区别是,它不仅限定于给定的方法和类,动态切入点还可以指定方法的参数。 当切入点需要在执行时根据参数值来调用通知时,就需要使用动态切入点。在大多数的 切入点可以使用静态切入点,很少有机会创建动态切入点。 7.1.6 Spring对事务的支持 事务的特性之一是原子(Atomic)性。如对数据库存取,就是一组SQL指令,这一组 SQL指令必须全部执行成功;如果因为某个原因(例如其中一行SQL有错误),则先前所 执行过的SQL指令撤销。 在JDBC中,可以用Connection的setAutoCommit()方法,给定它false参数。在一连 串的SQL语句后面,调用Connection的commit()来送出变更。如果中间发生错误,则调用 rollback()来撤销所有的执行,例如: try connection.setAutoCommit(false); /一连串SQL操作 mit(); /执行成功,提交所有变更 catch(SQLException e) connection.rollback(); /发生错误,撤销所有变更 7.1.6 Spring对事务的支持 在Spring中对JDBC的事务管理加以封装,Spring事务管理的抽象关键在于 org.springframwork.transaction.PlatformTransactionManager接口的实现。 PlatformTransactionManager接口有许多事务实现类别,如 DataSourceTransactionManager、HibernateTransactionManager、 JdoTransactionManager、JtaTransactionManager等。借助 PlatformTransactionManager接口和各种技术实现,Spring在事务管理上可以让开发人 员使用一致的编程模式。 事务的失败通常是致命的错误,Spring不强迫一定要处理,而让开发者自行选择是 否要捕捉异常。 Spring提供编程式的事务管理(Programmatic transaction management)与声明式的 事务管理(Declarative transaction management): 编程式的事务管理 编程式的事务管理可以清楚的控制事务的边界,即自行实现事务何时开始、撤销、 结束等,可以实现细力度的事务控制。 7.1.6 Spring对事务的支持 声明式的事务管理 然而在多数情况下,事务并不需要细粒度的控制,采用声明式的事务管理,优点是 Spring事务管理的相关API可以不用介入程序之中,从对象的角度来看,并不知道它正被 纳入事务管理之中,不需要事务管理的时候,只要在设定档案上修改一些设定,就可以移 除事务管理服务。 这里,主要介绍声明式事务管理。Spring的声明式事务管理依赖AOP框架来完成。使 用声明式事务管理的好处是事务管理不侵入开发组件。即DAO组件不会意识到正在事务管 理之中。如果想要改变事务管理策略,只需要在定义文档中重新组态即可。 例如,可以在不修改UserDAO类的情况下,为这个类加入事务管理的服务。一个 简化的方法是使用TransactionProxyFactoryBean,指定要介入的事务管理对象机器方法, 这里需要修改定义文档,如下所示: bean-config.xml代码如下: 7.1.6 Spring对事务的支持 TransactionProxyFactoryBean需要一个TransactionManager,如果是JDBC,可以 使用DataSourceTransactionManagr。由于这里使用的是Hibernate,所以使用 org.springframework.orm.hibernate3.HibernateTransactionManag。 TransactionProxyFactoryBean是代理类,“target”属性指定要代理的对象,事务管理会自 动介入指定的方法前后。这里是指“transactionAttributes”属性指定,insert*表示指定方法 名称insert开头的全部纳入事务管理。也可以指定方法全名,如果在方法执行过程中发生 错误,则所有先前的操作自动撤回,否则正常提交。 insert*等方法上指定“PROPAGATION_REQUIRED”,表示在目前的事务执行操作 中,如果事务不存在就创建一个新的,相关的意义可以在API文件中 TransactionDefinition接口中找到。可以加上多个事务定义,中间使用逗号“,”隔开。例如 ,可以加上只读,或者是指定某个例外发生时撤回操作: PROPAGATION_REQUIRED, readOnly,-MyCheckedException MyCheckedException前面加上“”时,表示发生指定异常撤销操作,如果加上“” ,表示发生异常时立即提交。 7.1.6 Spring对事务的支持 由于userDAO被userDAOProxy代理了,所以要做的是取得userDAOProxy,而不是 userDAO。例如: SpringAOPDemo.java import org.springframework.context.ApplicationContext; import org.springframework.context
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 新闻传播学媒体研究试题及答案
- 电商平台服务合同书
- 2025定制版手机的购销合同
- 天津专用2025届高考数学一轮复习考点规范练51随机抽样含解析新人教A版
- 2025房屋买卖合同书格式
- 2025年广告制作合同范本
- 2025(城市商业)租赁合同
- 2025【合作经营合同范本】
- 行政管理中的财务管理问题试题及答案
- 2025年企业移动应用开发合同官方版样本
- 2024年惠州市博罗县罗浮山文化旅游投资有限公司招聘笔试真题
- 中医特色治疗及护理
- 钢结构桁架厂房拆除施工方案
- 脑病科医护沟通技巧
- 四年级数学(小数加减运算)计算题专项练习与答案
- 《系统工程》复习题及答案
- 小区安全排查
- 中国典籍英译概述课件
- 【MOOC】航空发动机结构分析与设计-南京航空航天大学 中国大学慕课MOOC答案
- 红旅赛道未来规划
- 第七届江苏技能状元大赛无人机应用技术项目技术文件
评论
0/150
提交评论