文章目录

  • 一、AOP的概念
  • 二、切面的一些专业术语
  • 三、SpringAOP实战
    • 3.1 前期准备
    • 3.2 用XML配置切面
    • 3.3 xml配置文件如下
    • 3.4 测试代码
    • 3.4 基于注解的AOP开发
    • 3.5 环绕通知
  • 四、总结

一、AOP的概念

·  AOP(Aspect Oriented Programming)面向切面编程,是OOP面向对象编程的升级,AOP是基于OOP的。OOP比较好理解,生活中万物都是对象,但是AOP乍一看不是很好理解。不过没关系,我稍微一点拨,肯定就能懂。
·  说到底,其实就是“切面”这个概念我们不熟悉,我的理解是这样的:切面是某种对象的共同特征。什么意思呢?我举个简单的例子:我们一生中会玩很多的游戏,每个游戏都在不同的平台,那我们每次玩游戏,都要登录某个平台。面向对象编程的思维是这样的:我们这个人,就是一个对象,而我们玩游戏,就是这个对象使用了某种方法,所以在编程时我们把“人”这个概念抽取成了对象,把“玩游戏”这个概念抽取成一个个不同的方法。而在这个基础上,面向切面的编程方式 被提出了,因为我们发现,我们有无数种对应玩游戏的方法,但是每一种方法我们都需要进行登录的操作,所以我们把"登录"这个操作抽取出来,称作是所有"玩游戏"方法的切面,而每个玩游戏的方法,就是一个个切入点
·   可见,“登录”是所有“玩游戏”方法的共同特征,所以切面这个概念不如对象一般直观,因为,所有的切面都是人为抽取出来的,具有一定的思考性。而面向切面编程最重要的两个优势在于:1.可以简化代码量;2.可以让业务代码仅关注业务逻辑,使代码结构更清晰。
·  不管是哪一种优势,对于开发来说,都是至关重要的。

二、切面的一些专业术语

·  SpringAOP的实现是基于动态代理的,本人写过一遍有关动态代理的教程:Java两种动态代理实战+动态代理死循环的解释。
·  要会看的懂SpringAOP的相关文档就必须懂有关切面的相关术语,首先Aspect就是切面的意思。这个要是不知道,就快先去把6级考了再说。**
术语如下:

术语 含义
joinpoint 连接点 可以被拦截到的方法,但不一定会被增强
pointcut 切入点 被增强的方法(可见切入点一定是连接点,而反之则不然)
advice 通知(增强) 指在拦截到切入点后,如何增强某个方法
target 指被代理的对象
weaving 织入 指一个过程:增强某个类并形成代理类的过程
proxy 代理 一个类被织入后,就生成了一个代理类
aspect 切面 指切入点和通知的结合

·  如果你能把上述玩游戏的例子和这里的术语对应上,那你术语部分就过关啦!
其中advice术语是实现aop的关键,其有五种通知类型:

  • 1.前置通知(before):只在原方法之前调用的增强
  • 2.后置通知(after-returning):原方法完成之后调用的增强
  • 3.异常通知(after-throwing):原方法出异常后调用的增强
  • 4.最终通知(after):不管怎么样都会调用的增强,最后才会调用
  • 5.环绕通知(around):通过写代码来实现对切入点的管理(以上一般都是用配置,来决定顺序),环绕通知一般用于注解开发。

三、SpringAOP实战

·  本来想搞利用aop实现事务的管理的,不过既然在开头用了玩游戏的例子,那么索性,这里的案例就用玩游戏了。
·  需求如下:玩家只需要关注玩游戏就行。代理类的任务在于:在玩游戏之前实现登录操作,游戏结束后执行退出操作,如果玩游戏途中出现了游戏BUG,就执行回档操作(异常),最终不管怎么样,玩游戏是要钱的,最后要执行扣钱操作。

3.1 前期准备

·  引入jar包,aop需要的jar包是aspectjweaver,用于解析切入点表达式(下文会讲)以及spring-aop(这个一般引入spring-context依赖就会自己引入了),然后什么ioc的包等等,我就不说了,这次案例会用到一点点的IOC的知识,如果不会,可以参考我的IOC教程SpringIOC实战(xml+注解)。

  • 依赖如下:
   <dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.0.2.RELEASE</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.2</version></dependency></dependencies>
  • 配置玩家类(由Spring进行管理)
    可见我们希望切面能增强所有玩游戏的方法,而不增强吃饭这个方法,其中我们看见玩LOL时会出现异常。
package com.memoforward.player;import org.springframework.stereotype.Component;@Component
public class Player {public void playLOL(){System.out.println("玩英雄联盟...");throw new RuntimeException("LOL崩溃了....");}public void playDota2(){System.out.println("玩Dota2...");}public void playWOW(){System.out.println("玩魔兽世界...");}public void playTaiWu(){System.out.println("玩太吾绘卷...");}public void eat(){System.out.println("游戏玩累了,吃饭...");}
}
  • 配置通知类(由Spring进行管理)
package com.memoforward;import org.springframework.stereotype.Component;@Component("playerAdvice")
public class PlayerAdvice {//1.登录public void login(){System.out.println("---1.游戏登录---");}//2.退出public void quit(){System.out.println("---2.游戏退出---");}//3/异常回滚public void rollback(){System.out.println("---3.游戏回档---");}//4.扣钱public void loseMoney(){System.out.println("---4.游戏扣费---");}
}

3.2 用XML配置切面

·  使用SpringAOP需要在xml文件中引入aop的约束,这里就不贴了。
·  关于XML配置,共有如下的几个标签需要用到。

标签名 属性 作用 层级
aop:config 开启aop的控制 1
aop:pointcut id:指切入点表达式的id;expression:写切入点表达式 告诉切面将增强哪些方法(切入点) 2或3
aop:aspect id:该切面的id;ref:该切面对应的通知类 声明一个切面 2
aop : before method:该前置通知对应在通知类中的方法;pointcut-ref:切入点表达式id / pointcut:切入点表达式 声明一个前置通知 3
aop : after-returning 和上面类似 声明一个后置通知 3
aop : after-throwing 和上面类似 声明一个异常通知 3
aop : after 和上面类似 声明一个最终通知 3
  • 有关切入点表示的补充
    ·  切入点表达式有关键字:execution。在关键字内部写表达式,规则是:(访问修饰符 返回值 全限定类名.方法名),且不同的execution之间可以用**and、or、!**等关键字来增强表达式的逻辑。
    **举个例子:如果要切入点要选Player类中的playLOL方法,则表达式可以这么写:
 execution(public void com.memoforward.player.Player.playLOL())

·  显然这么写太复杂了,因此有如下的简化措施:

  1. 访问修饰符可省略
  2. 返回值可以用通配符 * 表示任意返回值类型
  3. 包名可以用 * 表示任意一个包;用 *. 表示当前包及其所有子包
  4. 类名和方法名都可以用 * 表示任意类和任意方法
  5. 可用 (..) 表示任意参数和任意参数类型(如果不想用用任意类型,基础类型可以直接写,引用类型用 ‘包名.类名’ 的方式)

`  因此有全通配写法:该项目下所有包的所有方法(不推荐使用)

execution(* *..*.*(..))
  • 一般情况下:我们只需要切到业务层实现类下的所有方法就可以了。

3.3 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/beans http://www.springframework.org/schema/beans/spring-beans.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="com.memoforward"/><aop:config><!--<aop:pointcut id="pt2" expression="..."/>--><aop:aspect id="playerAdvice" ref="playerAdvice"><aop:pointcut id="pt1" expression="execution(* com.memoforward.player.*.*(..)) and !execution(* com.memoforward.player.*.eat(..))"/><aop:before method="login" pointcut-ref="pt1"/><aop:after-returning method="quit" pointcut-ref="pt1"/><aop:after-throwing method="rollback" pointcut-ref="pt1"/><aop:after method="loseMoney" pointcut-ref="pt1"/></aop:aspect></aop:config></beans>
  • 注意事项:
  1. 用了两个execution语句实现了增强 除了eat方法外的所有方法。
  2. 注意切入点表达式的位置:如果在< aop: aspect >标签内,则这个表达式只对这个切面生效;如果在切面标签外,则对所有切面生效,但其必须要声明在切面之前。

3.4 测试代码

import ...@ContextConfiguration(locations = "classpath:beans.xml")
public class testSpringAOP extends AbstractTestNGSpringContextTests {@AutowiredPlayer player;@Testpublic void testAOP01(){player.eat();System.out.println("*************");player.playDota2();System.out.println("*************");player.playTaiWu();System.out.println("*************");player.playWOW();System.out.println("*************");player.playLOL();}
}
  • 测试结果如下:可见除了eat方法,其他方法都被增强了,而且出异常的LOL也成功进行了游戏回档。
游戏玩累了,吃饭...
*************
---1.游戏登录---
玩Dota2...
---2.游戏退出---
---4.游戏扣费---
*************
---1.游戏登录---
玩太吾绘卷...
---2.游戏退出---
---4.游戏扣费---
*************
---1.游戏登录---
玩魔兽世界...
---2.游戏退出---
---4.游戏扣费---
*************
---1.游戏登录---
玩英雄联盟...
---3.游戏回档---
---4.游戏扣费---java.lang.RuntimeException: LOL崩溃了....

3.4 基于注解的AOP开发

  • 基于注解的开发有两个步骤:
  1. 在配置文件中开启aop自动代理权限
  2. 配置切面的通知类
  • 配置文件开启权限
    <aop:aspectj-autoproxy/>
  • 配置切面通知类:@Aspect;@Pointcut;以及各种通知注解,很简单。
    注意:切入点表达式需要把 and 换成 &&
package com.memoforward;import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;@Component("playerAdvice02")
@Aspect
public class PlayerAdvice02 {@Pointcut("execution(* com.memoforward.player.*.*(..)) && !execution(* com.memoforward.player.*.eat(..))")private void pt1(){}//1.登录@Before("pt1()")public void login(){System.out.println("---游戏登录---");}//2.退出@AfterReturning("pt1()")public void quit(){System.out.println("---游戏退出---");}//3/异常回滚@AfterThrowing("pt1()")public void rollback(){System.out.println("---游戏回档---");}//4.扣钱@After("pt1()")public void loseMoney(){System.out.println("---游戏扣费---");}
}
  • 测试如下:此时已经换成了PlayerAdvice02切面。
import ...;@ContextConfiguration(locations = "classpath:beans.xml")
public class testSpringAOP extends AbstractTestNGSpringContextTests {@AutowiredPlayer player;@Testpublic void testAOP(){player.eat();System.out.println("*************");player.playTaiWu();System.out.println("*************");player.playLOL();}
}
  • 结果如下
游戏玩累了,吃饭...
*************
---1.游戏登录---
玩太吾绘卷...
---4.游戏扣费---
---2.游戏退出---
*************
---1.游戏登录---
玩英雄联盟...
---4.游戏扣费---
---3.游戏回档---java.lang.RuntimeException: LOL崩溃了....
  • 问题
    ·  仔细一点就能发现,最终通知和后置通知的顺序反了,这是注解开发的一个大问题,目前还没有被修复,因此如果要用注解开发的话,一般使用环绕通知的方式,所谓环绕通知和动态代理的实现方法基本没什么区别。下面将简单介绍一下:

3.5 环绕通知

·  环绕通知和动态代理的内部几乎是一样的写法,不同的点在于:动态代理的参数包含了被代理类的字节码对象;而在环绕通知中,因为Spring已经管理的被代理的类,因此就不必我们手动提供了,取而代之的,是Spring提供的的一个接口:ProceedingJoinPoint,此接口有两个方法,一个是获取被代理类方法的参数,一个是调用被代理类的方法。
代码如下:

    @Around("pt1()")public Object playerAdvice(ProceedingJoinPoint pjp){Object obj = null;try{Object[] args = pjp.getArgs();login();obj = pjp.proceed(args);quit();return obj;//注意pjp对象的方法需要用Throwable来处理异常} catch (Throwable throwable) {rollback();throw new RuntimeException(throwable);} finally {loseMoney();}
  • 测试结果如下:可见执行顺序已经恢复正常
游戏玩累了,吃饭...
*************
---1.游戏登录---
玩太吾绘卷...
---2.游戏退出---
---4.游戏扣费---
*************
---1.游戏登录---
玩英雄联盟...
---3.游戏回档---
---4.游戏扣费---java.lang.RuntimeException: java.lang.RuntimeException: LOL崩溃了....

四、总结

·  SpringAOP说难不难,但是重要的是这种面向切面的编程思想以及动态代理。Spring还剩最后一项事务管理。我会在下一次博客把它补上。

SpringAOP实战相关推荐

  1. SpringAOP实战和理论

    文章目录 Spring AOP概述 类和却面的关系 AOP术语 动态代理 Spring的通知类型 Spring 实现AOP 实战 XML实现 @Aspect注解实现 Spring AOP概述 AOP的 ...

  2. spring boot 实战 / 可执行war启动参数详解

    概述   上一篇文章<spring boot 实战 / mvn spring-boot:run 参数详解>主要讲解了spring boot 项目基于maven插件启动过程中借助profil ...

  3. SpringAOP中通过JoinPoint获取值,并且实现redis注解

    在Java8之前,代码编译为class文件后,方法参数的类型固定,但是方法名称会丢失,方法名称会变成arg0.arg1-..在Java8开始可以在class文件中保留参数名 public void t ...

  4. dubbo快速实战(非最佳配置,演示用)

    一.目标: 由于培训需要演示dubbo的快速实战,因此有了本文,再次声明,本文不是最佳配置. 使用dubbo,构建一个provider提供视频信息服务,一个consumer获取视频信息服务并调用. 文 ...

  5. tcc分布式事务框架源码解析系列(四)之项目实战

    通过之前的几篇文章我相信您已经搭建好了运行环境,本次的项目实战是依照happylifeplat-tcc-demo项目来演练,也是非常经典的分布式事务场景:支付成功,进行订单状态的更新,扣除用户账户,库 ...

  6. 从前慢-Mysql高级及实战

    Mysql高级及实战 1 Linux 系统安装MySQL 1.1 下载Linux 安装包 https://dev.mysql.com/downloads/mysql/5.7.html#download ...

  7. SSM 博客系统开发实战

    课程简介 SSM 框架即 SpringMVC+Spring+Mybatis,相比 SSH(Struts2+Spring+Hibernate)来说较新,SpringMVC 可与 Spring 更好的整合 ...

  8. 米米商城项目实战(含项目源码)

    目录 前言 1. 功能简介+项目展示 2. 数据库表 3. SSM框架 3.1 新建项目 3.2 目录改造 3.3 pom.xml文件 3.4 jdbc.properties 3.5 SqlMapCo ...

  9. Spring实战笔记——(1)Spring之旅(上)

    Spring实战笔记--(1)Spring之旅 文章目录 Spring实战笔记--(1)Spring之旅 1.1简化Java开发 1.1.1 激发POJO的潜能 1.1.2依赖注入 依赖注入的实现 构 ...

最新文章

  1. three.js 调用网络摄像头
  2. system generator学习笔记【02】
  3. Eclipse 搭建Android开发环境(整理)
  4. php怎么做免登录,php---一周内免登录
  5. flowable 多实例动态添加人
  6. 在虚拟机中安装linux6,如何在vmvare中安装redhat linux6虚拟机
  7. Regular expressions in lexing and parsing(翻译)
  8. 基于JAVA+SpringMVC+Mybatis+MYSQL的OA办公系统
  9. 数据结构与算法 3:二叉树,遍历,创建,释放,拷贝,求高度,面试,线索树
  10. Linux firefox2.0自动升级后启动不了
  11. docker配置网易云镜像
  12. .net core系列源码地址介绍
  13. 计算机科学美国大学专业,最新!2019年USNews美国大学计算机专业排名
  14. php入侵代码,入侵PHP网站就这么简单.pdf
  15. GEO数据库学习一(简介 数据下载 芯片知识)
  16. U盘可以被电脑识别,但无法读取U盘里的内容
  17. PCIe学习笔记之pcie结构和配置空间
  18. 一次变天之后的踏春之旅
  19. Java回炉学习(一)
  20. 打字游戏(极简单)——C/C++

热门文章

  1. python之三神器
  2. 开发微信公众号授权失败
  3. 计算机仿真后的数据怎么分析,仿真数据与流程管理全解读!
  4. 云台和华为p30pro_首款华为P30 Pro拍摄的轻科幻CG短片来了
  5. 新写的php到底是支持7.0还是7.1,PHP7.1.1和7.0.15正式发布,新功能抢眼
  6. Vue框架常用组件的快速构建项目Ctrl+c Ctrl+a Ctrl+v第十四课)
  7. 怎样在WORD中查看自己写了多少字
  8. java8获取某天凌晨时间戳,以获取第二天0点0分0秒时间点为例
  9. 前端技巧——手动调整文字的对齐
  10. cobbler自动化安装系统及配置