面向切面的Spring

来源:互联网 发布:linux 查看mysql 编辑:程序博客网 时间:2024/04/30 13:09

下班了,坐下来学习一会,单身狗狗回去也是无聊。昨天看了一下切面的知识,信息量有点多,脑子还是一片混 沌,就打算再看一遍,不过这次是边整理边看。
主要内容有:

             1. 什么是面向切面编程和AOP术语             2. Spring XML 编写切面             3. 使用@aspectJ 编写切面                                ## 什么是面向切面编程和AOP术语 ##

软件开发过程中分布于应用中的多处的功能被称为横切关注点,这些关注点与应用的业务逻辑时相分离的,将这些横切关注点和业务逻辑想分离是面向切面编程要解决的。是不是有点晦涩,举个例子是怎么回事呢,比如我们火车票事业部做火车票的业务,其中有个功能是发送邮件,发送邮件这个功能跟火车票的业务逻辑是不相干的,可能其他部门也要发送邮件这个功能,那么发送邮件就是横切关注点,把发送邮件作为一个切面嵌入到火车票业务逻辑中,可以实现分离。是不是有点小明白了,继续。

  AOP术语包括:

- 通知:描述切面的作用和什么时候起作用,即什么、何时
- 切点:描述切面作用的范围,即何处
- 切面:通知和切点的集合,即什么、何时、何处
- 连接点:能够插入切面的地方
- 引入:为现有的类添加新方法或属性
- 织入:将切面应用到现有对象来创建代理的过程

那么Spring是如何利用AOP 的呢?是不是也有这个疑问?答案就是Spring的AOP框架 采用一个包裹目标对象的代理类来执行切面的的逻辑,搜噶!遗憾的是Spring只支持方法的连接点,(也就是只能在方法级别插入切面,不能再方法内部插入,书上说的没我说的明白)但是基本上已经够用了。
## Spring XML 编写切面 ##
Spring借助AspectJ的切点表达式语音来定义Spring切面,Spring支持的指示器有:

arg() 限制连接点匹配参数 为 指定类型的执行方法
@args() 限制连接点匹配参数 由 指定注解标注的执行方法
execution() 用于匹配是连接点的执行方法
this() 限制连接点匹配AOP代理的Bean引用 为 指定的类型的类
target() 限制连接点匹配目标对象 为 指定类型的类
@target() 限制连接点匹配目标对象 为 特定的执行对象,这些对象要具备指定的类型的注解
within() 限制连接点匹配 指定的类型
@within() 限制连接点匹配 指定注解标注的类型
annotation 限制匹配 带有指定注解的连接点
举个编写切点的例子(切点是啥,看前面):
execution(com.springinaction.springidol.Instrument.play(…))&&within(com.springinaction.springidol. )
啥意思呢,就是当springidol包下的play()方法执行时,会触发通知,通知是啥,看前面。

其实还有一个bean()指示器,用Bean的ID来表示Bean。比如:
execution(*com.springinaction.springidol.Instrument.play(…)) and !bean(eddie)
啥意思,就是Bean的名字不是eddie的play()方法执行时,会触发通知,eddie的play()方法执行时不会触发通知,就这么简单。
一个POJO类声明为切面有两种方式,一种是在XML中,一种是使用@AspectJ注解。先看第一种。
语法:

定义AOP通知器
定义AOP后置通知
定义AOP after-returning通知
定义AOP after-throwing后置通知
定义AOP环绕通知
定义切面
启用@AspectJ 注解驱动的切面
定义AOP前置通知
顶层的AOP配置元素
为被通知的对象引入额外的借口
定义切点

看上去是不是有点晕,别担心,后面你就都会了。因为Example来了,例子一直让我很惊喜,因为例子的存在大大简化了我们的理解。
我们定义一个观众类:

pacage com.springinaction.springidol;public calss Audience{  public void takeSeats(){     system.out.printlin("the audience is taking their seats.")     }  public void turnOffCellPhones(){      system.out.printlin("the audience is turning their cellphones.")      }  public void applaud(){      system.out.printlin("CLAP CLAP CLAP CLAP CLAP CLAP.")      }  public void demandRefund(){      system.out.printlin("Boo! we want our money back!")      }}

然后XML把这个Bean引入

<bean id = "audience"   class  = "com.springinaction.springidol.Audience" />

下面才是我们的核心,在XML中把这个POJO声明为一个切面。

<aop:config>  <aop:aspect ref = "audience">  <aop:before pointcut =                "execution(*com.springinaction.springidol.performer.perform(...))" method = "takeSeats" />  <aop:before pointcut =                "execution(*com.springinaction.springidol.performer.perform(...))" method = "turnOffCellPhones" />  <aop:after-returning pointcut =                "execution(*com.springinaction.springidol.performer.perform(...))" method = "applaud" />  <aop:after-throwing pointcut =                "execution(*com.springinaction.springidol.performer.perform(...))" method = "demandRefund" />   </aop:aspect> </aop:config>

或者采用简化的方式:

<aop:config>  <aop:aspect ref = "audience">  <aop:pointcut id="performace" expression =               "execution(*com.springinaction.springidol.performer.perform(...))" method = "takeSeats" />  <aop:before        pointcut-ref ="performace"       method = "takeSeats" />  <aop:before        pointcut-ref ="performace"       method = "turnOffCellPhones" />  <aop:after-returning        pointcut-ref ="performace"        method = "applaud" />  <aop:after-throwing         pointcut-ref ="performace"        method = "demandRefund" />   </aop:aspect> </aop:config>

哼,还用得着解释么,是不是全明白了,继续加油↖(^ω^)↗。

声明环绕通知:
假如有个一个POJO

public void watchperformance(ProceedingJiontPoint jointpoint){ try{     system.out.printlin("the audience is taking their seats.");     system.out.printlin("the audience is turning their cellphones.");     long start = Syetem.currentTimeMillis();     jointpoint.proceed();     long end = System.currentTimeMillis();     system.out.printlin("CLAP CLAP CLAP CLAP CLAP CLAP.");     syetem.out.printlin("the perform took"+(end-start)+"milliseconds");    } catch (Throwable t){      system.out.printlin("Boo! we want our money back!"); }

XML如下:

<aop:config>  <aop:aspect ref = "audience">  <aop:pointcut id="performace2" expression =               "execution(*com.springinaction.springidol.performer.perform(...))" />  <aop:around        pointcut-ref ="performace2"       method = "watchPerformance()" />   </aop:aspect> </aop:config>

为通知传递一个参数:
加入一个切点有一个参数,那么怎么把这个参数传给通知呢?看下面:

<aop:config>  <aop:aspect ref = "audience">  <aop:pointcut id="performace3" expression =               "execution(*com.springinaction.springidol.performer.perform(string))" and args (dongfengpo) />  <aop:around        pointcut-ref ="performace3"       method = "watchPerformance"        arg-names = "dongfengpo"/>   </aop:aspect> </aop:config>

歌曲东风破 传过去了,嘎嘎。

通过切面引入新功能:
假如为所有的performer 引入一个新功能:

pacage com.springinaction.springidol;public interface Contestant{ void  receiveAward(); }

怎么办呢?

<aop:config>  <aop:aspect >  <aop:declare-parents       type-matching = "com.springinaction.springidol.Perfromer+"       implent-interface ="pacage com.springinaction.springidol.Contestant"       default-impl = "pacage com.springinaction.springidol.GraciousContestant"   </aop:aspect> </aop:config>

解释一下,就是 Perfromer接口会实现Contestant接口,具体接口的实现方法是GraciousContestant

使用@aspectJ 编写切面

直接上代码,如何把前面的audience 使用注解声明为一个切面

pacage com.springinaction.springidol;@Aspectpublic calss Audience{  @Pointcut("execution(*com.springinaction.springidol.performer.perform(...))")  public void performance(){ }  @Before(" performance()")  public void takeSeats(){     system.out.printlin("the audience is taking their seats.")     }  @Before(" performance()")  public void turnOffCellPhones(){      system.out.printlin("the audience is turning their cellphones.")      }  @AfterReturning(" performance()")      public void applaud(){      system.out.printlin("CLAP CLAP CLAP CLAP CLAP CLAP.")      }  @AfterThrowing(" performance()")      public void demandRefund(){      system.out.printlin("Boo! we want our money back!")      }}

跟之前一样,XML中把audience 注入进来

<bean id = "audience"   class  = "com.springinaction.springidol.Audience" />

最后要做的是让Spring把 audience应用为一个切面

<aop:aspectj-autoproxy />

这段声明会自动代理一些Bean ,这些Bean与使用@Aspect注解的Bean中用@Pointcut 注解的方法匹配

今天继续:
注解环绕通知,要使用@around注解

@Around("performance()")public void watchperformance(ProceedingJiontPoint jointpoint){ try{     system.out.printlin("the audience is taking their seats.");     system.out.printlin("the audience is turning their cellphones.");     long start = Syetem.currentTimeMillis();     jointpoint.proceed();     long end = System.currentTimeMillis();     system.out.printlin("CLAP CLAP CLAP CLAP CLAP CLAP.");     syetem.out.printlin("the perform took"+(end-start)+"milliseconds");    } catch (Throwable t){      system.out.printlin("Boo! we want our money back!"); }

和之前相比,区别就是POJO使用@Around标注,省去了XML中的配置

0 0
原创粉丝点击