Spring4 AOP详解

第一章Spring 快速入门并没有对Spring4 的 AOP 做太多的描述,是因为AOP切面编程概念不好理解。所以这章主要从三个方面详解AOP:AOP简介(了解),基于注解的AOP编程(重点)和基于xml的AOP编程。

1 AOP简介

1.1 什么是AOP

AOP(Aspect Oriented Programming)面向切面编程,是对传统的OOP(ObjectOriented Programming)面向对象编程的补充。

1.2 AOP的作用

如果A,B,C三个方法都要在执行前做验证操作,执行后做日志打印操作。肿么办?

排版好丑。。。。。。

1.3 AOP专业术语

切面(Aspect): A,B,C,方法执行前都要调用的验证逻辑和执行后都要调用的日志逻辑,这两个就是切面。

通知(Advice): 有五种通知,执行前,执行后,执行成功后,执行抛出异常后,环绕通知。就是切面执行的方法。

目标(Target): 被通知的对象,这里就是A,B,C三个方法。

连接点(Joinpoint):连接点是一个应用执行过程中能够插入一个切面的点。

切点(pointcut):每个类都拥有多个连接点,即连接点是程序类中客观存在的事务。AOP 通过切点定位到特定的连接点

打个比方:一天,三位侠客(被通知的对象Target)来我府上做客,被大门(切面Aspect)拦住,门前有五个保安(负责通知的Advice),因为其中一位侠客会降龙十八掌(满足被通知的一个条件Joinpoint),其中一位保安告知他:"你可以进去了"。另外两个侠客因为武艺超群(满足被通知的统一标准poincut)也都进去了。

2 基于注解的AOP编程

基于注解的编程,需要依赖AspectJ框架(java中最流行的aop框架)。

第一步:导入AspectJ的jar包,该框架只有Spring 2.0以上才支持。

<dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>4.2.2.RELEASE</version>
</dependency>

第二步:核心文件applicationContext.xml,里面需要配置自动扫描包(用于IOC注解)和配置启用 AspectJ注解

<?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:aop="http://www.springframework.org/schema/aop"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"><!-- 自动扫描的包 --><context:component-scan base-package="com.itdragon.spring.*" ></context:component-scan><!-- 使 AspectJ 的注解起作用 --><aop:aspectj-autoproxy></aop:aspectj-autoproxy></beans>

第三步:切面类,该类有什么特点?首先它必须是IOC的bean,还要声明它是 AspectJ切面,最后还可以定义切面的优先级Order(非必填)

通知有五种注解

@Before :前置通知的注解,在目标方法执行前调用

@After:后置通知的注解, 在目标方法执行后调用,即使程序抛出异常都会调用

@AfterReturning:返回通知的注解, 在目标方法成功执行后调用,如果程序出错则不会调用

@AfterThrowing:异常通知的注解, 在目标方法出现指定异常时调用

@Around:环绕通知的注解,很强大(相当于前四个通知的组合),但用的不多,

还有为了简化开发的重用切入点@Pointcut,以及抽象表达"*"

public interface Calculator {public int add(int a, int b);public int division(int a, int b);}
import org.springframework.stereotype.Repository;
@Repository("calculator")
public class CalculatorImp implements Calculator {@Overridepublic int add(int a, int b) {System.out.println("add 方法执行了 ----> " + (a + b));return (a + b);}@Overridepublic int division(int a, int b) {System.out.println("division 方法执行了 ----> " + (a / b));return (a / b);}}
import java.util.Arrays;
import java.util.List;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;/*** @Order(n) : 切面的优先级,n越小,级别越高* @Aspect:声明该类是一个切面* @Component:切面必须是 IOC 中的 bean*/
@Order(2)
@Aspect
@Component
public class LoggerAspect {/*** 前置通知的注解,在目标方法执行前调用* execution最基础的表达式语法。* 注意点:* 1. 方法里面不能有行参,及add(int a, int b) 这是会报错的。* 2. int(方法的返回值),add(方法名) 可以用 * 抽象化。甚至可以将类名抽象,指定该包下的类。* 3. (int, int) 可以用(..)代替,表示匹配任意数量的参数* 4. 被通知的对象(Target),建议加上包的路径*/@Before("execution(int com.atguigu.spring.my.aop.CalculatorImp.add(int , int))")public void beforeAdvice(JoinPoint joinPoint) {/*** 连接点 joinPoint:add方法就是连接点* getName获取的是方法名,是英文的,可以通过国际化转换对应的中文比较好。*/String methodName = joinPoint.getSignature().getName(); List<Object> args = Arrays.asList(joinPoint.getArgs());System.out.println("@Before 前置通知 : 方法名 【 " + methodName + " 】and args are " + args);}/*** 后置通知的注解, 在目标方法执行后调用,即使是程序出错都会调用* 这里将 方法的返回值 和 CalculatorImp类下所有的方法,以及方法的形参 都抽象了*/@After("execution(* com.atguigu.spring.my.aop.CalculatorImp.*(..))")public void afterAdvice(JoinPoint joinPoint) {String methodName = joinPoint.getSignature().getName(); List<Object> args = Arrays.asList(joinPoint.getArgs());System.out.println("@After 后置通知 : 方法名 【 " + methodName + " 】and args are " + args);}/*** 重用切入点定义:声明切入点表达式。该方法里面不建议添加其他代码*/@Pointcut("execution(* com.atguigu.spring.my.aop.CalculatorImp.*(..))")public void declareExecutionExpression(){}/*** 返回通知的注解, 在目标方法成功执行后调用,如果程序出错则不会调用* returning="result" 和 形参 result 保持一致*/@AfterReturning(value="declareExecutionExpression()", returning="result")public void afterRunningAdvice(JoinPoint joinPoint, Object result) {String methodName = joinPoint.getSignature().getName(); List<Object> args = Arrays.asList(joinPoint.getArgs());System.out.println("@AfterReturning 返回通知 : 方法名 【 " + methodName + " 】and args are " + args + " , result is " + result);}/*** 异常通知的注解, 在目标方法出现指定异常时调用* throwing="exception" 和 形参 exception 保持一致 , 且目标方法出了Exception(可以是其他异常)异常才会调用。*/@AfterThrowing(value="declareExecutionExpression()", throwing="exception")public void afterThrowingAdvice(JoinPoint joinPoint, Exception exception) {String methodName = joinPoint.getSignature().getName(); System.out.println("@AfterThrowing 异常通知 : 方法名 【 " + methodName + " 】and  exception is " + exception);}}
import java.util.Arrays;
import java.util.List;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;@Order(1)
@Aspect
@Component
public class AroundAspect {/*** 环绕通知,很强大,但用的不多。 用环绕通知测试Order的优先级看的不明显(这里是笔者的失误)* 环绕通知需要用ProceedingJoinPoint 类型的参数*/@Around("execution(* com.atguigu.spring.my.aop.CalculatorImp.*(..))")public Object aroundAdvice(ProceedingJoinPoint joinPoint) {Object result = null;String methodName = joinPoint.getSignature().getName(); List<Object> args = Arrays.asList(joinPoint.getArgs());try {System.out.println("@Around 前置通知 : 方法名 【 " + methodName + " 】and args are " + args);result = joinPoint.proceed();System.out.println("@Around 返回通知 : 方法名 【 " + methodName + " 】and args are " + args + " , result is " + result);} catch (Throwable e) {e.printStackTrace();System.out.println("@Around 异常通知 : 方法名 【 " + methodName + " 】and  exception is " + e);}System.out.println("@Around 后置通知 : 方法名 【 " + methodName + " 】and args are " + args);return result;}}
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {public static void main(String[] args) {ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");Calculator calculator = (Calculator) ctx.getBean("calculator");calculator.add(11, 12);calculator.division(21, 3); // 测试时,将被除数换成0,可以测试@AfterReturning , @After 和 @AfterThrowingctx.close();}}

第四步:执行看结果。这里没有做 环绕通知的打印。将被除数设置为零,可以测试 返回通知,后置通知 和 异常通知。

@Before 前置通知 : 方法名 【 add 】and args are [11, 12]
add 方法执行了 ----> 23
@After 后置通知 : 方法名 【 add 】and args are [11, 12]
@AfterReturning 返回通知 : 方法名 【 add 】and args are [11, 12] , result is 23
division 方法执行了 ----> 7
@After 后置通知 : 方法名 【 division 】and args are [21, 3]
@AfterReturning 返回通知 : 方法名 【 division 】and args are [21, 3] , result is 7

很简单对吧,用到的注解其实并不是很多。

以上代码有一个不足之处,就是测试Order优先级的时候,效果不明显。AroundAspect的优先级高于LoggerAspect,从打印的日志中发现,只有AroundAspect的前置通知在LoggerAspect前面打印,其他通知均在后面。

因为博客和课堂不同,如果把每个知识点都单独写出来,篇幅可能太长。笔者尽可能将所有知识点都写在一起,学A知识的同时将B,C,D的知识一起学习。但难免会有一些不听话的知识点。所以请各位读者见谅。

3 基于xml的AOP编程

上一篇文章讲到了基于xml的IOC设置bean,篇幅较长,内容较复杂。但配置AOP不同,它简单了很多。
第一步:核心文件applicationContext.xml,

首先是配置三个bean,方便是两个切面类,和一个方法类。

然后配置AOP,

aop:config:注明开始配置AOP了,

aop:pointcut:配置切点重用表达式,expression的值是具体的表达式,id 该aop:pointcut的唯一标识,

aop:aspect:配置切面,ref的值引用相关切面类的bean,order设置优先级(也可以不设置)。

五种通知的配置:aop:before,aop:after,aop:after-returning,aop:after-throwing,aop:around。method的值就是对应的方法,poincut-ref的值要引用 aop:pointcut 的id。其中有两个比较特殊:aop:after-returning 要多配置一个returning,其中returning的值要和对应方法的形参保持一致。同理aop:after-throwing 也要多配置一个throwing,其中throwing的值也要和对应方法的形参保持一致。不然执行程序会报错。

<?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:aop="http://www.springframework.org/schema/aop"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"><bean id="calculator" class="com.atguigu.spring.my.xml.CalculatorImp"></bean><bean id="loggerAspect" class="com.atguigu.spring.my.xml.LoggerAspect"></bean><bean id="aroundAspect" class="com.atguigu.spring.my.xml.AroundAspect"></bean><!-- AOP配置 --><aop:config><!-- 配置切点表达式 类似注解的重用表达式--><aop:pointcut expression="execution(* com.atguigu.spring.my.xml.CalculatorImp.*(..))" id="pointcut"/><!-- 配置切面及通知  method的值就是 loggerAspect类中的值--><aop:aspect ref="loggerAspect" order="2"><aop:before method="beforeAdvice" pointcut-ref="pointcut"/><aop:after method="afterAdvice" pointcut-ref="pointcut"/><aop:after-returning method="afterRunningAdvice" pointcut-ref="pointcut" returning="result"/><aop:after-throwing method="afterThrowingAdvice" pointcut-ref="pointcut" throwing="exception"/></aop:aspect><aop:aspect ref="aroundAspect" order="1"><!-- <aop:around method="aroundAdvice" pointcut-ref="pointcut"/>  --></aop:aspect></aop:config></beans>

第二步:下面几个类,就是脱去了所有注解的外衣,采用通过配置的xml,实现AOP编程。

public interface Calculator {public int add(int a, int b);public int division(int a, int b);}
public class CalculatorImp implements Calculator {@Overridepublic int add(int a, int b) {System.out.println("add 方法执行了 ----> " + (a + b));return (a + b);}@Overridepublic int division(int a, int b) {System.out.println("division 方法执行了 ----> " + (a / b));return (a / b);}}
import java.util.Arrays;
import java.util.List;
import org.aspectj.lang.JoinPoint;public class LoggerAspect {public void beforeAdvice(JoinPoint joinPoint) {String methodName = joinPoint.getSignature().getName(); List<Object> args = Arrays.asList(joinPoint.getArgs());System.out.println("Before 前置通知 : 方法名 【 " + methodName + " 】and args are " + args);}public void afterAdvice(JoinPoint joinPoint) {String methodName = joinPoint.getSignature().getName(); List<Object> args = Arrays.asList(joinPoint.getArgs());System.out.println("After 后置通知 : 方法名 【 " + methodName + " 】and args are " + args);}public void afterRunningAdvice(JoinPoint joinPoint, Object result) {String methodName = joinPoint.getSignature().getName(); List<Object> args = Arrays.asList(joinPoint.getArgs());System.out.println("AfterReturning 返回通知 : 方法名 【 " + methodName + " 】and args are " + args + " , result is " + result);}public void afterThrowingAdvice(JoinPoint joinPoint, Exception exception) {String methodName = joinPoint.getSignature().getName(); System.out.println("AfterThrowing 异常通知 : 方法名 【 " + methodName + " 】and  exception is " + exception);}}
import java.util.Arrays;
import java.util.List;
import org.aspectj.lang.ProceedingJoinPoint;public class AroundAspect {public Object aroundAdvice(ProceedingJoinPoint joinPoint) {Object result = null;String methodName = joinPoint.getSignature().getName(); List<Object> args = Arrays.asList(joinPoint.getArgs());try {System.out.println("@Around 前置通知 : 方法名 【 " + methodName + " 】and args are " + args);result = joinPoint.proceed();System.out.println("@Around 返回通知 : 方法名 【 " + methodName + " 】and args are " + args + " , result is " + result);} catch (Throwable e) {e.printStackTrace();System.out.println("@Around 异常通知 : 方法名 【 " + methodName + " 】and  exception is " + e);}System.out.println("@Around 后置通知 : 方法名 【 " + methodName + " 】and args are " + args);return result;}}
import org.springframework.context.support.ClassPathXmlApplicationContext;public class Main {public static void main(String[] args) {ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");Calculator calculator = (Calculator) ctx.getBean("calculator");calculator.add(11, 12);calculator.division(21, 3); // 测试时,将被除数换成0,可以测试AfterReturning ,After 和 AfterThrowingctx.close();}}
Before 前置通知 : 方法名 【 add 】and args are [11, 12]
add 方法执行了 ----> 23
After 后置通知 : 方法名 【 add 】and args are [11, 12]
AfterReturning 返回通知 : 方法名 【 add 】and args are [11, 12] , result is 23
Before 前置通知 : 方法名 【 division 】and args are [21, 3]
division 方法执行了 ----> 7
After 后置通知 : 方法名 【 division 】and args are [21, 3]
AfterReturning 返回通知 : 方法名 【 division 】and args are [21, 3] , result is 7

到这里,基于xml文件的AOP编程也讲完了。4不4很简单。
原文连接:http://blog.csdn.net/qq_19558705/article/details/50134755

更多干货等你来拿 http://www.itit123.cn/

Spring4 AOP详解相关推荐

  1. Spring AOP详解(转载)所需要的包

    上一篇文章中,<Spring Aop详解(转载)>里的代码都可以运行,只是包比较多,中间缺少了几个相应的包,根据报错,几经百度搜索,终于补全了所有包. 截图如下: 在主测试类里面,有人怀疑 ...

  2. Spring AOP详解(http://sishuok.com/forum/posts/list/281.html)

    三6.5  AspectJ切入点语法详解 6.5.1  Spring AOP支持的AspectJ切入点指示符 切入点指示符用来指示切入点表达式目的,,在Spring AOP中目前只有执行方法这一个连接 ...

  3. Spring面向切面编程-AOP详解

    文章目录 前言 介绍AOP 一.实现AOP 1.1.全注解形式实现AOP 前提准备(引入jar包) 实现AOP(五种通知) 二.认识JoinPont与ProceedingJoinPoint 2.1.初 ...

  4. Spring3.0 AOP 详解

    一.什么是 AOP. AOP(Aspect Orient Programming),也就是面向切面编程.可以这样理解,面向对象编程(OOP)是从静态角度考虑程序结构,面向切面编程(AOP)是从动态角度 ...

  5. spring教程--AOP详解

    1 Spring中的AOP 1.1 Spring的传统AOP : AOP:不是由Spring定义.AOP联盟的组织定义. Spring中的通知:(增强代码) 前置通知 org.springframew ...

  6. Spring AOP详解一文搞懂@Aspect、@Pointcut、@Before、@Around、@After、@AfterReturning、@AfterThrowing

    文章目录 1.AOP是什么 2.AOP中注解的含义 3.Pointcut切入点的语法 4.AOP代码实现 1.AOP是什么 AOP:Aspect Oriented Programming,翻译过来就是 ...

  7. Spring学习(八)AOP详解

    本文借鉴:Spring学习 一.一个例子 在上面的例子中,包租婆的核心业务就是签合同,收房租,那么这就够了,灰色框起来的部分都是重复且边缘的事,交给中介商就好了,这就是 AOP 的一个思想:让关注点代 ...

  8. 注解配置 AOP 详解

    注解通知的类型 通知的配置语法:@通知注解("切点表达式") 2) 切点表达式的抽取 同 xml配置aop 一样,我们可以将切点表达式抽取.抽取方式是在切面内定义方法,在该方法上使 ...

  9. 【Spring】Spring第二天 - AOP 详解、动态代理设计模式(JDK和cglib)

    一.AOP AOP 和 Filter 能够实现的 功能相似. AOP 和 Filter 的区别: AOP拦截的是类中方法(切点),只要方法能够被Spring管理,那么这个方法就能够被拦截. Filte ...

最新文章

  1. 语音识别(ASR)评估指标-WER(字错误率)和SER(句错误率)
  2. PHP--------解决网址URL编码问题
  3. 【科普】:10分钟看明白XML和JSON
  4. 代码居中对齐_HTML span标签如何居中和右对齐?这里有HTML span标签的样式解析
  5. 国际电信联盟:3GPP系标准成为唯一被认可的5G标准
  6. dmb: 数据库监控及灾备系统(for mysql)_MySQL企业级数据库灾备(备份)系统-DMB v2.1发布...
  7. 最邻近规则分类 KNN (K-Nearest Neighbor)算法及python实现
  8. sphinx mysql实时搜素_【原创】sphinx实时索引的配置及实际应用、实例
  9. 可视化神经网络实验报告,可视化神经网络工具
  10. 格兰杰因果检验_R实现
  11. 百度关键词指数查询易语言代码
  12. android 4.4.2 安全漏洞修复
  13. Python北京二手房房价数据集分析
  14. 五、组织机构模块-部门管理
  15. Js获取图片主色调,近似色,互补色,以及根据图片颜色获取主题配色方案详解、插件。
  16. 主页(一)01-搜索栏-顶部搜索栏布局
  17. 阿里p7程序员:生活压力大,有房贷不敢离职,离职股票就没了
  18. 容我缓缓神,记一次气的心脏病发作的经历
  19. opencv-python最全下载地址
  20. Eclipse中java文件图标变成空心J如何解决

热门文章

  1. Solidworks二次开发系列入门1
  2. C# SolidWorks二次开发-工程图-遍历选中视图中的草图
  3. 2022年华为杯中国研究生数学建模竞赛
  4. Java项目:健身管理系统设计和实现(Java+ssm+springboot)
  5. ant design vue,时间区间选择器
  6. FISU全球首家运动鞋通证商家联盟 9月5日隆重首发WBF交易所主板区
  7. javaScript基础练习题
  8. 城市信息模型(CIM)与人工智能技术如何有效结合?
  9. 为什么上班一天都是坐着,但仍会疲惫不堪?(转)
  10. 计算机机械硬盘的结构和工作原理