Spring学习笔记-C4-面向切面的Spring
来源:互联网 发布:php游戏开发教程 编辑:程序博客网 时间:2024/04/25 19:18
Chapter04 面向切面的Spring
4.1AOP所要解决的问题
将横切关注点(包裹在业务代码外层)与业务逻辑相分离,AOP实现将横切关注点与他们所影响的对象之间的解耦。除此之外,AOP还还会在声明式事务、安全和缓存进行应用。
AOP用于重用通用功能,传统方式最常见的是继承和委托。继承会造成对象体系非常脆弱,委托会对委托对象进行复杂的对象,AOP提供了另一种可选方案。我们通过声明式的方式定义这个功能要以何种方式在何处应用,而无需求改受影响的类。横切关注点可以被模块化为特殊的类,这些类被称为切面(aspect)。
优点:
1.每个关注点都集中于一个地方,而不是分散到代码中2.服务模块更简洁,只包含核心代码,关注点的代码都被转移到切面中
4.2 相关术语
- 通知(advice)
- 切点(pointcut)
- 连接点(joinpoint)
4.2.1 通知
切面的工作被称为通知。
- 前置通知(Before):在目标方法被调用之前调用通知功能
- 后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么
- 返回通知(After-returning):在目标方法成功执行之后调用通知
- 异常通知(After-throwing):在目标方法抛出异常后调用通知
- 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为
4.2.2 连接点
连接点是可以插入切面的一个点(时间点)。例如方法执行前、方法执行后、抛出异常时…等等。
4.2.3 切点
定义切面在何处(位置点)进行通知。
4.2.4 切面
切面是通知和切点的结合。
4.2.5 引入
引入允许我们向现有的类添加新的方法或属性。
4.2.6 织入
织入是把切面应用到目标对象并创建新的代理对象的过程。切面在指定的连接点被织入到目标对象中。
在目标对象的生命周期里有多个点可以进行织入:
- 编译期:切面在目标类编译时被织入。例如AspectJ的织入编译器。
- 类加载期:切面在目标被加载到JVM时被织入。例如AspectJ5.
- 运行期:切面在应用运行的某个时刻被织入。一般情况啊下,在织入切面时,AOP容器会为目标对象动态地创建一个代理对象。SpringAOP就是以这种方式织入切面的。(学习下反射和动态代理哦)
4.3 切点表达式语言
知道AspectJ是一种切点表达式语言就够啦。用起来超级简单。
以下注释部分是配置是说明如何通过AspectJ表达式来匹配该方法作为切点。
创建一个表演类,我们要在表演前后做一些动作
@Componentpublic class Performance{ /** * 执行该方法的切点表达是语言 execution(* chapter04.Performace.peforne(...)) * execution:表示执行该表达式语言 * * 表示任意返回类型 * chapter04.Performance.performe 分别是 包名、类名、方法名 * ...表示使用任意参数 * @Author NikoBelic * @Date 28/12/2016 19:04 */ public void perform() { System.out.println("我表演啦"); //return; }}
创建观众类作为切面
@Aspectpublic class Audience{ // 定义命名的切点 @Pointcut("execution(* *.perform(..))") public void performance() {} @Before("performance()") public void silenceCellPhones() { System.out.println("观众们请关闭手机铃声..."); } @Before("performance()") public void takSeats() { System.out.println("大家请坐..."); } @AfterReturning("performance()") public void applause() { System.out.println("大家请鼓掌..."); } @AfterThrowing("performance()") public void deamanRefund() { System.out.println("表演失败了,大家鼓励一下..."); }}
基于类的Spring配置
@Configuration@EnableAspectJAutoProxy@ComponentScanpublic class ConcertConfig{ @Bean public Audience audience() { return new Audience(); }}
测试方法
@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(classes = ConcertConfig.class)public class Main{ @Autowired Performance performance; @Test public void aopTest() { performance.perform(); }}
输出
观众们请关闭手机铃声...
大家请坐...
我表演啦
大家请鼓掌...
使用XML配置
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--配置自动扫包--> <context:component-scan base-package="chapter04"/> <!--启动AspectJ自动代理--> <aop:aspectj-autoproxy/></beans>
测试
@RunWith(SpringJUnit4ClassRunner.class)//@ContextConfiguration(classes = ConcertConfig.class)@ContextConfiguration(locations = "classpath:spring/c4-applicationContext.xml")public class Main{ @Autowired Performance performance; @Test public void aopTest() { performance.perform(); }}
环绕通知
修改切面类
@Aspectpublic class Audience{ // 定义命名的切点 @Pointcut("execution(* *.perform(..))") public void performance() {} @Around(("performance()")) public void watchPerformance(ProceedingJoinPoint joinPoint) { try { System.out.println("环绕-手机静音"); System.out.println("环绕-就坐"); joinPoint.proceed(); System.out.println("环绕-牛b牛b!再来一个!"); } catch (Throwable throwable) { System.out.println("环绕-垃圾!退钱!"); } } //@Before("performance()") public void silenceCellPhones() { System.out.println("观众们请关闭手机铃声..."); } //@Before("performance()") public void takSeats() { System.out.println("大家请坐..."); } //@AfterReturning("performance()") public void applause() { System.out.println("大家请鼓掌..."); } //@AfterThrowing("performance()") public void deamanRefund() { System.out.println("表演失败了,大家鼓励一下..."); }}
其他不变,测试结果 环绕-手机静音
环绕-就坐
我表演啦
环绕-牛b牛b!再来一个!
带参数的通知
我们通过编写切面来统计CD磁盘中的某个歌曲被播放了几次
播放器类
@Componentpublic class CDPlayer{ public void play(int songNum) { System.out.println("正在播放CD磁盘中第" + songNum + "首歌曲"); }}
切面类
@Aspectpublic class PlayCounter{ private HashMap<Integer,Integer> trackCounts = new HashMap<>(); @Pointcut("execution(* *.play(int)) " + "&& args(songNum)") public void trackPlayed(int songNum){} @Before("trackPlayed(songNum)") public void countSong(int songNum) { int currentCount = getPlayCount(songNum); trackCounts.put(songNum,currentCount + 1); System.out.println("该歌曲播放过" + currentCount + "次\n"); } public int getPlayCount(int songNum) { return trackCounts.containsKey(songNum)?trackCounts.get(songNum):0; }}
配置类
@Configuration@EnableAspectJAutoProxy/*启动AspectJ自动代理*/@ComponentScanpublic class ConcertConfig{ // 声明Audience bean @Bean public Audience audience() { return new Audience(); } @Bean public PlayCounter playCounter() { return new PlayCounter(); }}
测试
@Testpublic void aopArgsTest(){ for (int i = 0; i < 10; i++) { cdPlayer.play(new Random().nextInt(3)); }}
输出
该歌曲播放过0次正在播放CD磁盘中第2首歌曲该歌曲播放过1次正在播放CD磁盘中第2首歌曲该歌曲播放过0次正在播放CD磁盘中第0首歌曲该歌曲播放过2次正在播放CD磁盘中第2首歌曲该歌曲播放过3次正在播放CD磁盘中第2首歌曲该歌曲播放过1次正在播放CD磁盘中第0首歌曲该歌曲播放过2次正在播放CD磁盘中第0首歌曲该歌曲播放过4次正在播放CD磁盘中第2首歌曲该歌曲播放过3次正在播放CD磁盘中第0首歌曲该歌曲播放过0次正在播放CD磁盘中第1首歌曲
4.4 在XML中声明切面
当你需要声明切面,但又不能为通知类添加注解的时候,那么就必须转向XML配置了。
将切面的注解去掉
@Componentpublic class Audience{ // 定义命名的切点 //@Pointcut("execution(* *.perform(..))") //public void performance() //{} //@Around(("performance()")) public void watchPerformance(ProceedingJoinPoint joinPoint) { try { System.out.println("环绕-手机静音"); System.out.println("环绕-就坐"); joinPoint.proceed(); System.out.println("环绕-牛b牛b!再来一个!"); } catch (Throwable throwable) { System.out.println("环绕-垃圾!退钱!"); } } //@Before("performance()") public void silenceCellPhones() { System.out.println("观众们请关闭手机铃声..."); } //@Before("performance()") public void takSeats() { System.out.println("大家请坐..."); } //@AfterReturning("performance()") public void applause() { System.out.println("大家请鼓掌..."); } //@AfterThrowing("performance()") public void deamanRefund() { System.out.println("表演失败了,大家鼓励一下..."); }}
配置xml
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--配置自动扫包--> <context:component-scan base-package="chapter04"/> <!--启动AspectJ自动代理--> <aop:aspectj-autoproxy/> <aop:config> <!--切面--> <aop:aspect ref="audience"> <!--切点--> <aop:pointcut id="dosth" expression="execution(* *.perform(..))"/> <!--前置通知--> <aop:before method="silenceCellPhones" pointcut-ref="dosth"/> <aop:before method="takSeats" pointcut-ref="dosth"/> <!--后置通知--> <aop:after-returning method="applause" pointcut-ref="dosth"/> <!--异常通知--> <aop:after-throwing method="deamanRefund" pointcut-ref="dosth"/> </aop:aspect> </aop:config></beans>
注意 修改以下Main测试类的配置读取方式,将Java配置类改为配置文件配置。
@RunWith(SpringJUnit4ClassRunner.class)//@ContextConfiguration(classes = ConcertConfig.class)@ContextConfiguration(locations = "classpath:spring/c4-applicationContext.xml")public class Main{...}
5 小结
本章讲解了通过切面类和配置文件配置实现Spring的AOP功能,使模块之间进行解耦,有效减少了代码冗余,让我们只关注类自身的功能。
通过这几章我们学习了Spring和核心功能,DI和AOP。但是我们现在只停留在了会用的阶段,要深入其原理还需要看看源码,下一章开始学习SpringMVC。开始构建真正的Web应用。
- Spring学习笔记-C4-面向切面的Spring
- 《Spring实战》学习笔记-第四章:面向切面的Spring
- 《Spring实战》学习笔记-第四章:面向切面的Spring
- 4.Spring学习笔记之面向切面的Spring
- Spring学习笔记三:面向切面的Spring
- 《Spring实战》学习笔记(三)面向切面的Spring
- Spring学习笔记(三) 面向切面的Spring
- Spring学习笔记(八) --- 面向切面的Spring
- 《Spring实战》学习笔记-第四章:面向切面的Spring
- [Spring]面向切面编程AOP【学习笔记】
- Spring学习笔记(AOP面向切面编程)
- Spring学习笔记3--面向切面(AOP)的例子
- Spring学习笔记:面向切面(AOP)的基本定义
- 面向切面的Spring
- 面向切面的spring
- 面向切面的Spring
- 面向切面的Spring
- 面向切面的Spring
- leetcode-461-Hamming Distance
- 动力节点——继承(十二)
- 制作wordpress 导航栏
- 结构体
- bootstrapValidator插件使用
- Spring学习笔记-C4-面向切面的Spring
- python软件使用
- 加载第三方apk的资源文件,
- Android中如何使用Fragment打造出炫酷效果
- PL/SQL Developer主数据库连接和窗口连接切换
- Hadoop对各个节点的角色定义
- 如何用pycharm来调试odoo?
- skip-grant-tables:非常有用的mysql启动参数
- OTA--卡刷全包、差分升级包制作、分析(代码摘自Google)---2