什么是 AOP

AOP(Aspect-Oriented Programming):面向切面编程,是对传统的面向对象编程的补充。
什么意思呢?

比如上图中,在不同的方法中,有许多相同的功能代码,那我们就可以把这些相同的功能代码抽取出来,放到类中,那么这个类就被叫做切面。

实际上,AOP 的原理就是利用了动态代理,当我们需要调用目标对象的时候,Spring 就会帮我们生成一个代理对象,将切面和核心的业务逻辑代码组装起来,形成完整的模块。即使我们将代码抽离出来,也并不会影响我们的正常使用。

这样做的好处是:

  1. 业务模块更简洁,只包含核心业务代码,在我们编码的时候就可以更加专注于核心代码的编写上。其次在维护、调试的时候也更加容易定位问题的所在。
  2. 当我们需要修改公共功能的代码时,只需修改公共功能所在的切面即可,不必再去一个一个的方法中修改。

注解配置

对于面向切面编程,我们可以使用 ASpectJ 框架,AspectJ 是 Java 社区里最完整最流行的 AOP 框架。

准备工作

因此我们想要使用 AscpectJ 框架,就必须要先导入依赖的 jar 包:

  • aopalliance.jar
  • aspectj.weaver.jar
  • spring-aspects.jar

其次我们还需要将 aop Schema 命名空间添加到配置文件中。

基于注解的 AOP

接下来我们就可以使用注解实现 AOP 了。

面向切面编程,那么首先我们得有切面。上文说到切面就是一个类,那难道说我们创建了一个类,这个类就是一个切面吗?Spring 如何识别这是一个切面呢?

我们可以使用 @Aspect 注解,只要在对应的类上标注这个注解,那么此类就是一个切面。

切面中是一个一个的通知,一个切面中可以有多个通知,通知就是切面要完成的工作,在我们想要调用业务方法时,会将这些通知加入到业务方法中的某个位置,比如方法前、后等,从而形成一个完整的业务功能。通知在代码中的体现就是加了某种注解的 Java 方法。

AspectJ 一共支持 5 种类型的通知,它们对应的注解分别是:

  • @Before:前置通知,在方法执行之前执行。
  • @After:后置通知,在方法执行之后执行,无论方法中是否有异常都会执行。
  • @AfterRunning:返回通知,在方法返回结果之后执行。
  • @AfterThrowing:异常通知,在方法抛出异常之后执行。
  • @Around:环绕通知,需要我们自己在方法中控制。

前置通知

(1)

@Before
public void beforeMethod(JoinPoint joinPoint){String methodName = joinPoint.getSignature().getName();Object [] args = joinPoint.getArgs();System.out.println("The method " + methodName + " begins with " + Arrays.asList(args));
}

(2)

配置了通知之后,还缺一样东西,把这些通知应用到哪些方法上呢,我们要告诉 Spring,所以我们还要配置切入点表达式。
例如:

@Before("execution(public int com.spring.aop.ArithmeticCalculator.*(..))")

以下是一些示例:

表达式 含义
execution(* com.atguigu.spring.ArithmeticCalculator.*(…)) ArithmeticCalculator 接口中声明的所有方法。第一个“*”代表任意修饰符及任意返回值。第二个“*”代表任意方法。“…”匹配任意数量、任意类型的参数。若目标类、接口与该切面类在同一个包中可以省略包名。
execution(public * ArithmeticCalculator.*(…)) ArithmeticCalculator 接口的所有公有方法。
execution(public double ArithmeticCalculator.*(…)) ArithmeticCalculator 接口中返回double类型数值的方法。
execution(public double ArithmeticCalculator.*(double, …)) 第一个参数为double类型的方法。“…” 匹配任意数量、任意类型的参数。
execution(public double ArithmeticCalculator.*(double, double)) 参数类型为 double,double 类型的方法。
execution (* *.add(int,…)) || execution(* *.sub(int,…)) 切入点表达式可以通过 “&&”、“||”、“!”等操作符结合起来。

(3)

除此之外,要想让这些注解起作用,还需要在配置文件中配置一样东西。在 Spring IOC 容器中启用 AspectJ 注解支持,需要在配置文件中定义一个空的 XML 元素:

<!-- 配置自动为匹配 aspectJ 注解的 Java 类生成代理对象 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

当 Spring IOC 容器侦测到 bean 配置文件中的 <aop:aspectj-autoproxy> 元素时,会自动为与 AspectJ 切面匹配的 bean 创建代理。

JoinPoint 类
通过 JoinPoint 类我们就可以访问一些链接细节,比如当前方法的名称、参数等。

后置通知

@After()
public void afterMethod(JoinPoint joinPoint){String methodName = joinPoint.getSignature().getName();System.out.println("The method " + methodName + " ends");
}

返回通知

在返回通知中,可以访问到方法的返回值,只需要将 returning 属性加入到 @AfterReturning 注解中,返回值就会传给 returning 属性对应的值。此外还要在方法中添加一个同名参数。

@AfterReturning(value="declareJointPointExpression()",returning="result")
public void afterReturning(JoinPoint joinPoint, Object result){String methodName = joinPoint.getSignature().getName();System.out.println("The method " + methodName + " ends with " + result);
}

异常通知

异常通知中,可以定义发生何种异常时,才执行异常通知,并且可以访问到异常对象。和返回通知类似,我们需要在注解中添加 throwing 属性,以及在方法中添加一个和 throwing 属性值同名的参数,此参数即指定了发生何种异常执行此通知。

/*** 在目标方法出现异常时会执行的代码.* 可以访问到异常对象;,且可以指定在出现特定异常时再执行通知代码*/
@AfterThrowing(value="declareJointPointExpression()",
throwing="e")
public void afterThrowing(JoinPoint joinPoint, Exception e){String methodName = joinPoint.getSignature().getName();System.out.println("The method " + methodName + " occurs excetion:" + e);
}

环绕通知

环绕通知类似于动态代理的全过程,需要我们手动控制在何时(方法前?后?)执行什么代码。

与上述通知不同的是:环绕通知的连接点参数类型必须是 ProceedingJoinPoint,它是 JoinPoint 的子接口,如果想要执行被代理的方法,必须调用 ProceedingJoinPointproceed() 方法。此外,环绕通知还必须有返回值,返回值即为目标方法的返回值,即 ProceedingJoinPoint.proceed() 方法的返回值。

/*** 环绕通知需要携带 ProceedingJoinPoint 类型的参数. * 环绕通知类似于动态代理的全过程: ProceedingJoinPoint 类型的参数可以决定是否执行目标方法.* 且环绕通知必须有返回值, 返回值即为目标方法的返回值
*/
@Around("execution(public int com.atguigu.spring.aop.ArithmeticCalculator.*(..))")
public Object aroundMethod(ProceedingJoinPoint pjd){Object result = null;String methodName = pjd.getSignature().getName();try {//前置通知System.out.println("The method " + methodName + " begins with " + Arrays.asList(pjd.getArgs()));//执行目标方法result = pjd.proceed();//返回通知System.out.println("The method " + methodName + " ends with " + result);} catch (Throwable e) {//异常通知System.out.println("The method " + methodName + " occurs exception:" + e);throw new RuntimeException(e);}//后置通知System.out.println("The method " + methodName + " ends");return result;
}

切入点可重用

如果我们在每一个注解的后面都指定切入点表达式,则非常麻烦,如果修改还需要一个一个修改。因此我们可不可以将切入点表达式抽离出来呢?

答案是:可以的。我们可以使用 @Pointcut 注解来配置统一的切入点表达式,我们只需要在一个方法上方用 @Pointcut 注解标注,其他的注解直接引用该方法名即可。

切入点方法的访问控制符同时也控制着这个切入点的可见性。如果切入点要在多个切面中共用,最好将它们集中在一个公共的类中。在这种情况下,它们必须被声明为 public。在引入这个切入点时,必须将类名也包括在内。如果类没有与这个切面放在同一个包中,还必须包含包名。

/*** 定义一个方法, 用于声明切入点表达式。 一般地,该方法中再不需要添入其他的代码。* 使用 @Pointcut 来声明切入点表达式。* 后面的其他通知直接使用方法名来引用当前的切入点表达式。*/
@Pointcut("execution(public int com.spring.aop.ArithmeticCalculator.*(..))")
public void declareJointPointExpression(){}@Before("declareJointPointExpression()")

配置切面的优先级

如果我们有好几个切面,Spring 就不知道谁改先执行,谁该后执行。不过,我们可以明确指定它们之间的执行顺序,切面的优先级可以通过实现 Ordered 接口或利用 @Order 注解指定。

实现 Ordered 接口,getOrder() 方法的返回值越小,优先级越高。@Order 注解也类似,数值越小,优先级越高。

@Aspect
@Order(1)
public class LoggingAspect {}

XML 配置

切面除了支持注解配置,还支持使用配置文件的方式来配置。不过正常情况下,基于注解的声明要优先于基于 XML 的声明。

(1)
和注解配置类似,首先我们也要配置一个切面。切面所在的类要先实例化。

<!-- 配置切面的 bean. -->
<bean id="loggingAspect" class="com.atguigu.spring.aop.xml.LoggingAspect"></bean><!-- 配置 AOP -->
<aop:config><!-- 配置切面 --><aop:aspect ref="loggingAspect" order="2"></aop:aspect>
</aop:config>

(2)
第二步我们要配置切入点表达式。使用 <aop:pointcut> 标签,如果配置在 <aop:config> 标签下,则所有的切面都可使用,如果配置在 <aop:aspect> 标签下,则只能在此切面中使用。

<!-- 配置 AOP -->
<aop:config><!-- 配置切点表达式 --><aop:pointcut id="pointcut" expression="execution(*com.spring.aop.xml.ArithmeticCalculator.*(int, int))" />
</aop:config>

(3)
第三步就是配置各个通知了,每种通知都对应这不同的 aop 标签,在通知中可以使用 pointcut 属性来单独配置切入点表达式,也可以使用 pointcut-ref 属性来引用已经配置好的切入点表达式。

<!-- 配置切面的 bean. -->
<bean id="loggingAspect" class="com.atguigu.spring.aop.xml.LoggingAspect"></bean><bean id="vlidationAspect" class="com.atguigu.spring.aop.xml.VlidationAspect"></bean><!-- 配置 AOP -->
<aop:config><!-- 配置切点表达式 --><aop:pointcut id="pointcut" expression="execution(* com.spring.aop.xml.ArithmeticCalculator.*(int, int))" /><!-- 配置切面及通知 --><aop:aspect ref="loggingAspect" order="2"><aop:before method="beforeMethod" pointcut-ref="pointcut"/><aop:after method="afterMethod" pointcut-ref="pointcut"/><aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="e"/><aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result"/><!--  <aop:around method="aroundMethod" pointcut-ref="pointcut"/>--></aop:aspect>   <aop:aspect ref="vlidationAspect" order="1"><aop:before method="validateArgs" pointcut-ref="pointcut"/></aop:aspect>
</aop:config>

Spring 核心 之 AOP相关推荐

  1. spring核心之AOP学习总结二

    一:springAOP常用的注解 @Aspect:声明方面组件 @Before:声明前置通知 @After-returning:声明后置通知 @After:声明最终通知 @Around:声明环绕通知 ...

  2. Spring核心部分之AOP,aspectJ框架实现AOP,切入点表达式

    1. 面向切面编程(Aspect Oriented Programming,AOP) AOP:以切面为核心,确定切面的执行时间以及执行位置,底层实现是动态代理. AOP的作用:①在不改动源代码的情况下 ...

  3. Spring核心机制IoC与AoP梳理

    Spring核心机制IoC与AoP梳理 文章目录 Spring核心机制IoC与AoP梳理 IoC介绍 IoC案例介绍 pom文件中IoC环境引入 自己new对象方法举例(正转) IoC创建对象 基于X ...

  4. Spring核心AOP(面向切面编程)总结

    (尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/75208354冷血之心的博客) 目录 1.AOP概念: 2.AOP特 ...

  5. 一文读懂Spring中的AOP机制

    一.前言 这一篇我们来说一下 Spring 中的 AOP 机制,为啥说完注解的原理然后又要说 AOP 机制呢? 1.标记日志打印的自定义注解 @Target({ElementType.METHOD}) ...

  6. Spring核心——IOC处理器扩展

    为什么80%的码农都做不了架构师?>>>    非侵入式框架 Spring一直标注自己是一个非侵入式框架.非侵入式设计的概念并不新鲜,目标就是降低使用者和框架代码的耦合,毕竟框架的开 ...

  7. spring核心功能结构

    spring核心功能结构 Spring大约有20个模块,由1300多个不同的文件构成.这些模块可以分为: 核心容器.AOP和设备支持.数据访问与集成.Web组件.通信报文和集成测试等, 下面是 Spr ...

  8. Spring(IOC+AOP)

    Spring 容器(可以管理所有的组件(类))框架:        核心关注:IOC和AOP: Test:Spring的单元测试模块: spring-test-4.0.0.RELEASE Core C ...

  9. Spring源码-AOP(六)-自动代理与DefaultAdvisorAutoProxyCreator

    2019独角兽企业重金招聘Python工程师标准>>> Spring AOP 源码解析系列,建议大家按顺序阅读,欢迎讨论 Spring源码-AOP(一)-代理模式 Spring源码- ...

  10. 小马哥spring编程核心思想_Spring源码高级笔记之——Spring核心思想

    Spring核心思想 注意:IOC和AOP不是spring提出的,在spring之前就已经存在,只不过更偏向于理论化,spring在技术层次把这两个思想做了非常好的实现(Java) 第1节loC 1. ...

最新文章

  1. 用Zabbix部署onalert云告警平台
  2. python中fn的用法_Pytorch技巧:DataLoader的collate_fn参数使用详解
  3. java中executorservice_java中ExecutorService创建方法总结
  4. 先装VS2008再装SQL2005的问题
  5. 【必看】做了3年运维却不涨薪?那是你还没get这个技能
  6. 超级计算机的电力消耗,适用超级计算机的一种优化供电方式
  7. kubeadm reset后安装遇到的错误:Unable to connect to the server: x509: certificate signed by unknown authority
  8. Java与Python的区别告诉你,学什么看自己
  9. sre8 sre10_是什么使SRE出色?
  10. 秋叶一键重装系统连接服务器失败,如何解决远程桌面无法连接
  11. 时间操作(Java版)—获取距离系统时间N天后的日期时间信息
  12. windows下mongodb安装与使用
  13. B key-M key-BM key
  14. 红外图像特点及识别方法
  15. 黑客工具包ShadowBrokers浅析
  16. codesys工程ST语言学习笔记(一)建立工程与编译
  17. 【软件测试】时制转换时的电话账单
  18. 手机的1G,2G,3G和4G区别识记
  19. REVA世界巡回交流会——亚太峰会 澳门站拉开序幕
  20. 脑洞大开的思维工具:PMI

热门文章

  1. WebGL入门(十二)-使用varying变量在顶点着色器给片元着色器传值,绘制彩色三角形
  2. 程序员鄙视链, 所有工程师都鄙视php工程师, 为什么
  3. 【渝粤教育】国家开放大学2019年春季 1366英语教学理论与实践 参考试题
  4. 联想服务器rd640性能,至强E5芯动力 联想RD640服务器评测
  5. 7步走!做出高质量的数据分析项目
  6. 微信小程序:微信公众号申请微信小程序并认证的步骤
  7. 基于Vue使用Arco Design组件封装一个七牛云上传图片的函数
  8. 博客备份系统之一:PDF,Word,TXT文件操作类
  9. 出现Illegal invocation的报错
  10. C/C++编程学习 - 第22周 ② 非负数的和