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