您的位置:首页 > 编程语言 > Java开发

面向切面的Spring

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

主要内容有:

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;
@Aspect
public 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中的配置
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: