第7章_在Spring中管理事务

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

第7章在Spring中管理事务7.1知识点:AOP简介7.2开发步骤7.1知识点:AOP简介7.1.1从代理机制初探AOP从一个简单常见的例子开始。这个例子当中含有日志动作,程序中经常需要为某些动作或事件作下记录,以便随时检查程序运行过程和排除错误的信息。来看一个简单的例子,当需要在执行某些方法时留下日志信息,直觉的,可能会这样写。importjava.util.logging.*;publicclassHelloSpeaker{pirvateLoggerlogger=Logger.getLogger(this.getClass().getName());publicvoidhello(Stringname){logger.log(Level.INFO,”hellomethodstarts…”);//方法执行开始时留下日志Sytem.out.println(”hello,”+name);//程序的主要功能Logger.log(Level.INFO,”hellomethodends…”);//方法执行完毕时留下日志}}7.1.1从代理机制初探AOP在HelloSpeaker类中,当执行hello()方法时,程序员希望开始执行该方法与执行完毕时都会留下日志,最简单的做法是用上面的程序设计,在方法执行的前后加上日志动作。然而对于HelloSpeaker来说,日志的这种动作并不属于HelloSpeaker逻辑,这使得HelloSpeaker增加了额外的职责。如果程序中这种日志动作到处都有需求,以上的写法势必造成程序员必须到处撰写这些日志动作的代码。这将使得维护日志代码的困难加大。如果需要的服务不只是日志动作,有一些非类本身职责的相关动作也混入到类中,比如权限检查,事务管理等等,会使得类的负担加重,甚至混淆类本身的职责。另一方面,使用以上的写法,如果有一天不再需要日志(或权限检查,交易管理等)的服务,将需要修改所有留下日志动作的程序,无法简单地就将这些相关服务从现有的程序中移除。可以使用代理(Proxy)机制来解决这个问题,有两种代理方式:静态代理(staticproxy)和动态代理(dynamicproxy)。在静态代理的实现中,代理类与被代理的类必须实现同一个接口,在代理类中可以实现记录等相关服务,并在需要的时候再呼叫被代理类。这样被代理类中就可以仅仅保留业务相关的职责了。7.1.1从代理机制初探AOP举个简单的例子,首先定义一个IHello接口:IHello.java代码如下:publicinterfaceIhello{publicvoidhello(Stringname);}然后让实现业务逻辑的HelloSpeaker类别实现Ihello接口,HelloSpeaker.java代码如下:publicclassHelloSpeakerimplementsIhello{publicvoidhello(Stringname){Sytem.out.println(”hello,”+name);}}可以看到,在HelloSpeaker类中没有任何日志的代码插入其中,日志服务的实现将被放到代理类中,代理类同样要实现IHello接口:7.1.1从代理机制初探AOPHelloProxy.java代码如下:publicclassHelloProxyimplementsIhello{privateLoggerlogger=Logger.getLogger(this.getClass().getName());privateIhellohelloObject;publicHelloProxy(IhellohelloObject){this.helloObject=helloObject;}publicvoidhello(Stringname){log(”hellomethodstarts…”);//日志服务helloObject.hello(name);//执行业务逻辑log(”hellomethodends…”);//日志服务privatevoidlog(Stringms){logger.log(Level.INFO,msg);}}7.1.1从代理机制初探AOP在HelloProxy类的hello()方法中,真正实现业务逻辑前后可以安排记录服务,可以实际撰写一个测试程序来看看如何使用代理类。publicclassProxyDemo{publicstaticvoidmain(String[]args){IHelloproxy=newHelloProxy(newHelloSpeaker());proxy.hello(”Justin”);}}这是静态代理的基本示例,但是可以看到,代理类的一个接口只能服务于一种类型的类,而且如果要代理的方法很多,势必要为每个方法进行代理,静态代理在程序规模稍大时就必定无法胜任。7.1.2动态代理在JDK1.3之后加入了可协助开发动态代理功能的API等相关类别,不需要为特定类和方法编写特定的代理类,使用动态代理,可以使得一个处理者(Handler)为各个类服务。要实现动态代理,同样需要定义所要代理的接口:IHello.java代码如下:publicinterfaceIHello{publicvoidhello(Stringname);}然后让实现业务逻辑的HelloSpeaker类别要实现IHello接口,如HelloSpeaker.java代码如下:publicclassHelloSpeakerimplementsIHello{publicvoidhello(Stringname){System.out.println(”Hello,”+name);}}7.1.2动态代理写一个测试程序,如果要使用LogHandler的bind()方法来绑定被代理类。ProxyDemo.java代码如下:publicclassProxyDemo{publicstaticvoidmain(String[]args){LogHandlerlogHandler=newLogHandler();IHellohelloProxy=(IHello)logHandler.bind(newHelloSpeaker());helloProxy.hello(”Justin”);}}HelloSpeaker本身的职责是显示文字,却必须插入日志动作,这使得HelloSpeaker的职责加重。在AOP的是术语中,日志的程序代码横切(cross-cutting)到HelloSpeaker的程序执行流程中,日志这样的动作在AOP中被称为横切关注点(cross-cuttingconcern)。使用代理类将记录与业务逻辑无关的动作提取出来,设计为一个服务类,如同前面的范例HelloProxy或者LogHandler,这样的类称之为切面(aspect)。AOP中的aspect所指的可以是像日志等这类的动作或服务,将这些动作(cross-cuttingconcern)设计为通用,不介入特定业务类的一个职责清楚的Aspect类,这就是所谓的Aspect-orientedprogramming,AOP。7.1.3AOP术语与概念介绍AOP术语和概念。●Cross-cuttingconcer在DynamicProxyDemo的例子中,记录的动作原先被横切(Cross-cutting)到HelloSpeaker本身所负责的业务流程中。另外类似于日志这类的动作,如安全检查、事务等服务,在一个应用程序中常被安排到各个类的处理流程之中。这些动作在AOP的术语中被称为Cross-cuttingconncers。如图7-1所示,原来的业务流程是很单纯的。图7-1原来的业务流程7.1.3AOP术语与概念Cross-cuttingconcerns如果直接写在负责某业务的类的流程中,使得维护程序的成本增加。如果以后要把类的记录功能修改或者移除这些服务,则必须修改所有撰写曾记录服务的程序,然后重新编译。另一方面,Cross-cuttingconcerns混杂在业务逻辑之中,使得业务类本身的逻辑或者程序的撰写更为复杂。如同7-2所示,为了要加入日志与安全检查等服务,类的程序代码中被硬生生地写如了相关的Logging、Security程序片段。图7-2加入各种服务的业务流程7.1.3AOP术语与概念●Aspect将散落在各个业务类中的Cross-cuttingconcerns收集起来,设计各个独立可重用的类,这种类称之为Aspect。例如在动态代理中将日志的动作设计为一个LogHandler类,LogHandler类在AOP术语中就是Aspect的一个具体实例。在需要该服务的时候,缝合到应用程序中;不需要服务的时候,也可以马上从应用程序中脱离。应用程序中的可重用组件不用做任何的修改,例如在动态代理中的HelloSpeaker所代表的角色就是应用程序中可重用的组件,在它需要日志服务时并不用修改本身的程序代码。另一方面,对于应用程序中可重用的组件来说,以AOP的设计方式,它不用知道处理提供服务的类的存在。即与服务相关的API不会出现在可重用的应用组件中,因而可提高这些组件的重用性,可以将这些组件应用到其他的应用程序中,而不会因为目前加入了某个服务或与目前的应用框架发生耦合。不同的AOP框架对AOP概念有不同的实现方式,主要的差别在于所提供的Aspects的丰富程度,以及它们如何被缝合(Weave)到应用程序中。7.1.4通知AdviceSpring提供了5种通知(Advice)类型:InterceptionAround、Before、AfterReturning、Throw和Introduction。它们分别在以下情况被调用:InterceptionAroundAdvice:在目标对象的方法执行前后被调用。BeforeAdvice:在目标对象的方法执行前被调用。AfterReturningAdvice:在目标对象的方法执行后被调用ThrowAdvice:在目标对象的方法抛出异常时被调用。IntroductionAdvice:一种特殊类型的拦截通知,只有在目标对象的方法调用完毕后执行。这里,用过前置通知BeforeAdvice来说明。BeforeAdvice会在目标对象的方法执行之前被呼叫。如同在便利店里,在客户购买东西之前,老板要给它们一个热情的招呼。为了实现这一点,需要扩展MethodBeforAdvice接口。这个接口提供了获取目标方法、参数以及目标对象。publicinterfaceMethodBeforeAdvice{voidbefore(Methodmethod,Object[]args,Objecttarget)throwsThrowable}7.1.4通知Advice用实例来示范如何使用BeforeAdvice。首先要定义目标对象必须实现的接口:IHello.java代码如下:publicinterIHello{publicvoidhello(Stringname);}接着定义一个HelloSpeaker了,让它实现IHello接口:HelloSpeaker.java代码如下:publicclassHelloSpeakerimplementsIHello{publicvoidhello(Stringname){System.out.println(“Hello,”+name);}}在对HelloSpeader不进行任何修改的情况下,想要在hello()方法执行之前,可以记录一些信息,有一个组件,但是没有源代码,但是想对它增加一些日志的服务。可以先实现MethodBeoforeAdvice接口,例如:7.1.4通知AdviceLogBeforeAdvice.java代码如下:importjava.lang.reflect.Method;importjava.util.logging.Level;importjava.util.logging.Logger;importorg.

1 / 32
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功