- 作者简介
- 内容提要
- 关于本书
- 路线图
- 代码规范与下载
- 作者在线
- 封面插图简介
- 前言
- 译者序
- 致谢
- 第1部分 Spring 的核心
- 第1章 Spring 之旅
- 第2章 装配 Bean
- 第3章 高级装配
- 第4章 面向切面的 Spring
- 第2部分 Web 中的 Spring
- 第5章 构建 Spring Web 应用程序
- 第6章 渲染 Web 视图
- 第7章 Spring MVC 的高级技术
- 第8章 使用 Spring Web Flow
- 第9章 保护 Web 应用
- 第3部分 后端中的 Spring
- 第10章 通过 Spring 和 JDBC 征服数据库
- 第11章 使用对象-关系映射持久化数据
- 第12章 使用 NoSQL 数据库
- 第13章 缓存数据
- 第14章 保护方法应用
- 第4部分 Spring 集成
- 第15章 使用远程服务
- 第16章 使用 Spring MVC 创建 REST API
- 第17章 Spring消息
- 第18章 使用 WebSocket 和 STOMP 实现消息功能
- 第19章 使用 Spring 发送 Email
- 第20章 使用 JMX 管理 Spring Bean
- 第21章 借助 Spring Boot 简化 Spring 开发
4.3.1 定义切面
如果一场演出没有观众的话,那不能称之为演出。对不对?从演出的角度来看,观众是非常重要的,但是对演出本身的功能来讲,它并不是核心,这是一个单独的关注点。因此,将观众定义为一个切面,并将其应用到演出上就是较为明智的做法。
程序清单4.1展现了Audience类,它定义了我们所需的一个切面。
程序清单4.1 Audience类:观看演出的切面
Audience类使用@AspectJ注解进行了标注。该注解表明Audience不仅仅是一个POJO,还是一个切面。Audience类中的方法都使用注解来定义切面的具体行为。
Audience有四个方法,定义了一个观众在观看演出时可能会做的事情。在演出之前,观众要就坐(takeSeats())并将手机调至静音状态(silenceCellPhones())。如果演出很精彩的话,观众应该会鼓掌喝彩(applause())。不过,如果演出没有达到观众预期的话,观众会要求退款(demandRefund())。
可以看到,这些方法都使用了通知注解来表明它们应该在什么时候调用。AspectJ提供了五个注解来定义通知,如表4.2所示。
表4.2 Spring使用AspectJ注解来声明通知方法
注 解 | 通 知 |
@After | 通知方法会在目标方法返回或抛出异常后调用 |
@AfterReturning | 通知方法会在目标方法返回后调用 |
@AfterThrowing | 通知方法会在目标方法抛出异常后调用 |
@Around | 通知方法会将目标方法封装起来 |
@Before | 通知方法会在目标方法调用之前执行 |
Audience使用到了前面五个注解中的三个。takeSeats()和silence CellPhones()方法都用到了@Before注解,表明它们应该在演出开始之前调用。applause()方法使用了@AfterReturning注解,它会在演出成功返回后调用。demandRefund()方法上添加了@AfterThrowing注解,这表明它会在抛出异常以后执行。
你可能已经注意到了,所有的这些注解都给定了一个切点表达式作为它的值,同时,这四个方法的切点表达式都是相同的。其实,它们可以设置成不同的切点表达式,但是在这里,这个切点表达式就能满足所有通知方法的需求。让我们近距离看一下这个设置给通知注解的切点表达式,我们发现它会在Performance的perform()方法执行时触发。
相同的切点表达式我们重复了四遍,这可真不是什么光彩的事情。这样的重复让人感觉有些不对劲。如果我们只定义这个切点一次,然后每次需要的时候引用它,那么这会是一个很好的方案。
幸好,我们完全可以这样做:@Pointcut注解能够在一个@AspectJ切面内定义可重用的切点。接下来的程序清单4.2展现了新的Audience,现在它使用了@Pointcut。
程序清单4.2 通过@Pointcut注解声明频繁使用的切点表达式
在Audience中,performance()方法使用了@Pointcut注解。为@Pointcut注解设置的值是一个切点表达式,就像之前在通知注解上所设置的那样。通过在performance()方法上添加@Pointcut注解,我们实际上扩展了切点表达式语言,这样就可以在任何的切点表达式中使用performance()了,如果不这样做的话,你需要在这些地方使用那个更长的切点表达式。我们现在把所有通知注解中的长表达式都替换成了performance()。
performance()方法的实际内容并不重要,在这里它实际上应该是空的。其实该方法本身只是一个标识,供@Pointcut注解依附。
需要注意的是,除了注解和没有实际操作的performance()方法,Audience类依然是一个POJO。我们能够像使用其他的Java类那样调用它的方法,它的方法也能够独立地进行单元测试,这与其他的Java类并没有什么区别。Audience只是一个Java类,只不过它通过注解表明会作为切面使用而已。
像其他的Java类一样,它可以装配为Spring中的bean:
如果你就此止步的话,Audience只会是Spring容器中的一个bean。即便使用了AspectJ注解,但它并不会被视为切面,这些注解不会解析,也不会创建将其转换为切面的代理。
如果你使用JavaConfig的话,可以在配置类的类级别上通过使用EnableAspectJ-AutoProxy注解启用自动代理功能。程序清单4.3展现了如何在JavaConfig中启用自动代理。
程序清单4.3 在JavaConfig中启用AspectJ注解的自动代理
假如你在Spring中要使用XML来装配bean的话,那么需要使用Spring aop命名空间中的<aop:aspectj-autoproxy>元素。下面的XML配置展现了如何完成该功能。
程序清单4.4 在XML中,通过Spring的aop命名空间启用AspectJ自动代理
不管你是使用JavaConfig还是XML,AspectJ自动代理都会为使用@Aspect注解的bean创建一个代理,这个代理会围绕着所有该切面的切点所匹配的bean。在这种情况下,将会为Concertbean创建一个代理,Audience类中的通知方法将会在perform()调用前后执行。
我们需要记住的是,Spring的AspectJ自动代理仅仅使用@AspectJ作为创建切面的指导,切面依然是基于代理的。在本质上,它依然是Spring基于代理的切面。这一点非常重要,因为这意味着尽管使用的是@AspectJ注解,但我们仍然限于代理方法的调用。如果想利用AspectJ的所有能力,我们必须在运行时使用AspectJ并且不依赖Spring来创建基于代理的切面。
到现在为止,我们的切面在定义时,使用了不同的通知方法来实现前置通知和后置通知。但是表4.2还提到了另外的一种通知:环绕通知(around advice)。环绕通知与其他类型的通知有所不同,因此值得花点时间来介绍如何进行编写。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论