spring --AOP
在说AOP之前,先来看看切片面包,我们是把面包看成一个对象,而如果想在面包中间夹点番茄酱或者其他什么东西怎么办呢,是不是需要把面包切片,在每一片都要夹点东西,在编程中给对象重复性 执行某一动作是不是特别繁琐也降低了效率。今天要学习的AOP能很好的解决这种问题 。
一、AOP
AOP(Aspect-Oriented Programming,面向切面编程):是一种新的方法论,是对传统 OOP(Object-Oriented Programming,面向对象编程)的补充。
传统OOP是面向对象的编程,针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分。将面包看成一个对象,执行面包对象本身的业务逻辑,而在面包的每一片前后添加番茄酱这种反复横向的动作如果实现起来就导致大量代码的重复。
而AOP是将对象横向的切面看成一个处理对象,只要找到面包横向切入的点,也就是需要知道在哪里添加番茄酱,AOP自动切入,执行添加番茄酱的动作,而且对于每一片面包执行添加的动作都是一样的。这就像编程中需要给逻辑业务添加日志,现在专门找一个AOP切面类从横向方向考虑给每一个业务逻辑添加日志。AOP处理某个步骤和阶段,从中进行切面的提取,也就是说,如果几个或更多个逻辑过程中,有重复的操作行为,AOP就可以提取出来,运用动态代理,实现程序功能的统一维护。
这么一看AOP与OOP取长补短,相互补充。
这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。
二、AOP的核心概念
1.横向关注点
对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点
2. 切面(Aspect)
封装横切关注点信息的类,每个关注点体现为一个通知方法。
3.连接点(joinpoint)
横切关注点在程序代码中的具体体现,对应程序执行的某个特定位置,比如方法调用的时候或者异常的时候都是连接点。spring AOP中,一个连接点总是表示一个方法的执行。例如:类某个方法调用前、调用后、方法捕获到异常后等。
4.通知(Advice)
通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类;许多AOP框架(包括Spring)都是以拦截器做通知模型,并维护一个以连接点为中心的拦截器链。
5. 目标(Target)
被通知的对象,这个对象为真正对象的代理对象。
6.代理(Proxy)
AOP框架创建的对象,用来实现切面契约(例如通知方法执行等等)。
三、通知类型:
前置通知(Before advice):在某连接点之前执行的通知,但这个通知不能阻止连接点之前的执行流程(除非它抛出一个异常)。
后置通知(After returning advice):在某连接点正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。
异常通知(After throwing advice):在方法抛出异常退出时执行的通知。
最终通知(After (finally) advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
环绕通知(Around Advice):包围一个连接点的通知,如方法调用。这是最强大的一种通知类型。环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它自己的返回值或抛出异常来结束执行。
四、AOP原理---动态代理
参照:http://www.cnblogs.com/Actexpler-S/p/7455649.html (动态代理)
http://www.cnblogs.com/Actexpler-S/p/7469607.html (代理模式)
下图是我自己对AOP实现原理的理解
五、Spring AOP 实现
在Spring2.5.6中,常用的AOP实现的两种方法:
第一种,是基于注解方式实现的。
第二种,是基于xml配置文件方式的实现。
情景举例
①数学计算器接口[MathCalculator]
public interface MathCaculator {public int add(int i,int j);public int sub(int i,int j);public int multi(int i,int j);public int div(int i,int j); }
②提供简单实现[EasyImpl]
@Repository public class RawCaculator implements MathCaculator{@Overridepublic int add(int i, int j) {int result=i+j;System.out.println("add方法执行");return result;}@Overridepublic int sub(int i, int j) {System.out.println("sub方法执行");return i-j;}@Overridepublic int multi(int i, int j) {System.out.println("multi方法执行");return i*j;}@Overridepublic int div(int i, int j) {System.out.println("div方法执行");return i/j;}}
③在简单实现的基础上让每一个计算方法都能够打印日志[LoginImpl]
(一)Spring中可以使用注解的方式实现AOP。
1)导入jar包
com.springsource.net.sf.cglib -2.2.0.jarcom.springsource.org.aopalliance-1.0.0 .jarcom.springsource.org.aspectj.weaver-1.6.8 .RELEASE.jarcommons-logging-1.1.3. jarspring-aop-4.0.0.RELEASE.jarspring-aspects-4.0.0.RELEASE.jarspring-beans-4.0.0.RELEASE.jarspring-context-4.0.0.RELEASE.jarspring-core-4.0.0.RELEASE.jarspring-expression-4.0.0.RELEASE. jar
2)开启基于注解的AOP功能
< aop:aspectj-autoproxy />
3)声明一个切面类,并把这个切面类加入到IOC容器中
在切面类中声明通知方法
[1]前置通知:@Before
[2]返回通知:@AfterReturning
[3]异常通知:@AfterThrowing
[4]后置通知:@After
[5]环绕通知:@Around :环绕通知是前面四个通知的集合体!
package com.neuedu.spring.aop;import static org.hamcrest.CoreMatchers.nullValue;import java.util.Arrays; import java.util.List;import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; 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;/* * 项目名称:spring-aop01 * @author:wzc * @date 创建时间:2017年8月29日 上午9:51:01 * @Description://表明当前类是一个切面类 * @parameter * */ @Component @Aspect @Order(value=50) public class Caculatoraspest {/** 前置方法,可以获取目标方法名以及方法的参数,*/@Before(value="execution(public int com.neuedu.spring.aop.MathCaculator.*(..))")
public void showBeginlog(JoinPoint Point){//getArgs 获取参数Object[] args = Point.getArgs();//将参数转换成listList<Object> asList=Arrays.asList(args);//getSignature获取方法的签名Signature signatureame = Point.getSignature();///获取方法的名String name=signatureame.getName();System.out.println("[日志]【方法开始】目标方法名为:"+name +",参数为:"+asList);}@After(value="showLog()")public void showAfterLog(){System.out.println("[日志]【方法正常结束】");}@AfterThrowing(value="showLog()",throwing ="ex")public void showExceptionlog(JoinPoint Point,Exception ex){System.out.println("[日志]【方法异常】"+ex.getMessage());}/* 返回通知,可以获取方法执行的返回值*/@AfterReturning(value="execution(public int com.neuedu.spring.aop.MathCaculator.*(..))")public void showAfter(JoinPoint Point,Object result){System.out.println("[日志]【方法最终结束】目标方法的返回值为:"+result);}/** 环绕通知* * */@Around(value="execution(public int com.neuedu.spring.aop.MathCaculator.*(..))")public Object AroundShowLog(ProceedingJoinPoint Point){Object result=null;//getArgs 获取参数Object[] args = Point.getArgs();//将参数转换成listList<Object> asList=Arrays.asList(args);//getSignature获取方法的签名Signature signatureame = Point.getSignature();///获取方法的名String name=signatureame.getName();try {try {System.out.println("[日志]【方法开始】目标方法名为:"+name +",参数为:"+asList);result=Point.proceed(args);} finally {System.out.println("[日志]【方法执行结束】");}System.out.println("[日志]【方法最终结束】目标方法的返回值为:"+result); } catch ( Throwable e) {System.out.println("[日志]【方法异常】" +e.getMessage());}return result; }}
测试一下
@Testpublic void test() {ApplicationContext ioc=new ClassPathXmlApplicationContext("applicationContext.xml");MathCaculator bean = (MathCaculator) ioc.getBean("rawCaculator");bean.add(10, 5);System.out.println();bean.sub(10, 5);System.out.println();bean.multi(10, 5);System.out.println();bean.div(10, 5);System.out.println();}
结果:
因为在切面类中还定义了环绕通知,所以每个方法的日志记录有两条
基于注解的切入点表达式
切入点表达式的语法格式
execution([权限修饰符] [返回值类型] [简单类名/全类名] [方法名]([参数列表])) |
最详细的切入点表达式:
execution(public int com.neuedu.aop.target.MathCalculatorImpl.add(int, int))
最模糊的切入点表达式:
execution (* *.*(..))
第一个“*”代表任意修饰符及任意返回值。
第二个“*”代表若目标类、接口与该切面类在同一个包中可以省略包名。
第三个“*”代表任意方法。
“..”匹配任意数量、任意类型的参数。
在AspectJ中,切入点表达式可以通过 “&&”、“||”、“!”等操作符结合起来。
表达式 |
execution (* *.add(int,..)) || execution(* *.sub(int,..)) |
含义 |
任意类中第一个参数为int类型的add方法或sub方法 |
统一声明切入点表达式
@Pointcut(value="execution(public int com.neuedu.spring.aop.MathCaculator.*(..))")
public void showLog(){}
package com.neuedu.spring.aop;import static org.hamcrest.CoreMatchers.nullValue;import java.util.Arrays; import java.util.List;import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; 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;/* * 项目名称:spring-aop01 * @author:wzc * @date 创建时间:2017年8月29日 上午9:51:01 * @Description://表明当前类是一个切面类 * @parameter * */ @Component @Aspect @Order(value=50) public class Caculatoraspest {@Pointcut(value="execution(public int com.neuedu.spring.aop.MathCaculator.*(..))")public void showLog(){ }/** 前置方法,可以获取目标方法名以及方法的参数,*/@Before(value="showLog()")public void showBeginlog(JoinPoint Point){//getArgs 获取参数Object[] args = Point.getArgs();//将参数转换成listList<Object> asList=Arrays.asList(args);//getSignature获取方法的签名Signature signatureame = Point.getSignature();///获取方法的名String name=signatureame.getName();System.out.println("[日志]【方法开始】目标方法名为:"+name +",参数为:"+asList);}@After(value="showLog()")public void showAfterLog(){System.out.println("[日志]【方法正常结束】");}@AfterThrowing(value="showLog()",throwing ="ex")public void showExceptionlog(JoinPoint Point,Exception ex){System.out.println("[日志]【方法异常】"+ex.getMessage());}/* 返回通知,可以获取方法执行的返回值*/@AfterReturning(value="showLog()",returning="result")public void showAfter(JoinPoint Point,Object result){System.out.println("[日志]【方法最终结束】目标方法的返回值为:"+result);}/** 环绕通知* * */@Around(value="execution(public int com.neuedu.spring.aop.MathCaculator.*(..))")public Object AroundShowLog(ProceedingJoinPoint Point){Object result=null;//getArgs 获取参数Object[] args = Point.getArgs();//将参数转换成listList<Object> asList=Arrays.asList(args);//getSignature获取方法的签名Signature signatureame = Point.getSignature();///获取方法的名String name=signatureame.getName();try {try {System.out.println("[日志]【方法开始】目标方法名为:"+name +",参数为:"+asList);result=Point.proceed(args);} finally {System.out.println("[日志]【方法执行结束】");}System.out.println("[日志]【方法最终结束】目标方法的返回值为:"+result); } catch ( Throwable e) {System.out.println("[日志]【方法异常】" +e.getMessage());}return result; }}
通知方法的细节
①在通知中获取目标方法的方法名和参数列表
[1]在通知方法中声明一个JoinPoint类型的形参
[2]调用JoinPoint对象的getSignature()方法获取目标方法的签名
[3]调用JoinPoint对象的getArgs()方法获取目标方法的实际参数列表
②在返回通知中获取方法的返回值
[1]在@AfterReturning注解中添加returning属性
@AfterReturning (value="myPointCut()", returning= "result")
[2]在返回通知的通知方法中声明一个形参,形参名和returning属性的值一致
showReturnLog(JoinPoint joinPoint, Object result)
③在异常通知中获取异常对象
[1]在@ AfterThrowing注解中添加throwing属性
@AfterThrowing (value="myPointCut()",throwing= "throwable" )
[2]在异常通知的通知方法中声明一个形参,形参名和throwing属性值一致
showExceptinLog(JoinPoint joinPoint, Throwable throwable)
环绕通知:@Around
1.环绕通知需要在方法的参数中指定JoinPoint的子接口类型ProceedingJoinPoint为参数
@Around(value="pointCut()")
public void around(ProceedingJoinPoint joinPoint){
}
2.环绕通知会将其他4个通知能干的,自己都给干了!所以写了环绕通知就不用写其他4个通知了。
注意:@Around修饰的方法一定要将方法的返回值返回!本身相当于代理!
(二)基于XML配置的AOP
<!-- 1.将需要加载到IOC容器中的bean配置好 --><bean id="twoCaculator" class="com.neuedu.spring.aop.TwoCaculator"></bean><bean id="oneAspect" class="com.neuedu.spring.aop.OneAspect"></bean><!-- 2.配置AOP,需要导入AOP名称空间 --><aop:config><!-- 配置切面表达式 --><aop:pointcut expression="execution(public int com.neuedu.spring.aop.TwoCaculator.*(..))" id="Pointcut"/><!-- 配置OneAspect切面 --><aop:aspect ref="oneAspect"><!-- 通过method属性指定切面类的切面方法,通过pointcut-ref指定切入点表达式 --><aop:before method="showBeginlog" pointcut-ref="Pointcut"/><aop:after method="showAfterLog" pointcut-ref="Pointcut"/><aop:after-throwing method="showExceptionlog" throwing="ex" pointcut-ref="Pointcut"/><aop:after-returning method="showAfter" pointcut-ref="Pointcut" returning="result"/></aop:aspect></aop:config>
切面类
package com.neuedu.spring.aop;import java.util.Arrays; import java.util.List;import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature;/** 项目名称:spring-aop01 * @author:wzc * @date 创建时间:2017年8月29日 下午3:49:42 * @Description: * @parameter * */ public class OneAspect {public void showBeginlog(JoinPoint Point){//getArgs 获取参数Object[] args = Point.getArgs();//将参数转换成listList<Object> asList=Arrays.asList(args);//getSignature获取方法的签名Signature signatureame = Point.getSignature();///获取方法的名String name=signatureame.getName();System.out.println("[日志]【方法开始】目标方法名为:"+name +",参数为:"+asList);}public void showAfterLog(){System.out.println("[日志]【方法正常结束】");}public void showExceptionlog(JoinPoint Point,Exception ex){System.out.println("[日志]【方法异常】"+ex.getMessage());}/* 返回通知,可以获取方法执行的返回值*/public void showAfter(JoinPoint Point,Object result){System.out.println("[日志]【方法最终结束】目标方法的返回值为:"+result);}}
测试一下:
@Testpublic void test() {ApplicationContext ioc=new ClassPathXmlApplicationContext("applicationContext.xml");MathCaculator bean= (MathCaculator)ioc.getBean("twoCaculator");bean.add(10, 5);System.out.println();bean.sub(10, 5);System.out.println();bean.multi(10, 5);System.out.println();bean.div(10, 5);System.out.println();}
结果:
环绕通知的XML配置和上面的四种通知的 配置一样
<aop:aspect ref="twoAspext"><aop:around method="AroundShowLog" pointcut-ref="Pointcut"/></aop:aspect>
六、多切面
(一)优先级
我们可以声明多个切面类,它们可以同时应用在同一个Target上,但是问题出现了,它们的执行顺序是怎么样的,Spring AOP提供了一个注解用于解决这个问题,@Order,该注解只有一个字段值,默认是整数最大值,也就是最小优先级,说明它的值越小,优先级越高。
基于配置的也可以加入order
(二)执行的顺序问题
spring aop就是一个同心圆,要执行的方法为圆心,最外层的order最小。从最外层按照AOP1、AOP2的顺序依次执行doAround方法,doBefore方法。然后执行method方法,最后按照AOP2、AOP1的顺序依次执行doAfter、doAfterReturn方法。也就是说对多个AOP来说,先before的,一定后after。
转载于:https://www.cnblogs.com/Actexpler-S/p/7490063.html
spring --AOP相关推荐
- Spring AOP + Redis解决重复提交的问题
Spring AOP + Redis解决重复提交的问题 用户在点击操作的时候,可能会连续点击多次,虽然前端可以通过设置按钮的disable的属性来控制按钮不可连续点击,但是如果别人拿到请求进行模拟,依 ...
- 利用Spring AOP与JAVA注解为系统增加日志功能
Spring AOP一直是Spring的一个比较有特色的功能,利用它可以在现有的代码的任何地方,嵌入我们所想的逻辑功能,并且不需要改变我们现有的代码结构. 鉴于此,现在的系统已经完成了所有的功能的开发 ...
- Spring AOP的一些概念
切面(Aspect): 一个关注点的模块化,这个关注点可能会横切多个对象.事务管理是J2EE应用中一个关于横切关注点的很好的例子. 在Spring AOP中,切面可以使用通用类(基于模 ...
- Spring AOP与IOC
Spring AOP实现日志服务 pom.xml需要的jar <dependency><groupId>org.apache.commons</groupId>&l ...
- Spring AOP与IOC以及自定义注解
Spring AOP实现日志服务 pom.xml需要的jar <dependency><groupId>org.apache.commons</groupId>&l ...
- Spring Aop的应用
2019独角兽企业重金招聘Python工程师标准>>> AOP的基本概念 连接点( Jointpoint) : 表示需要在程序中插入横切关注点的扩展点,连接点可能是类初始化.方法执行 ...
- Spring AOP详解(转载)所需要的包
上一篇文章中,<Spring Aop详解(转载)>里的代码都可以运行,只是包比较多,中间缺少了几个相应的包,根据报错,几经百度搜索,终于补全了所有包. 截图如下: 在主测试类里面,有人怀疑 ...
- 关于spring aop Advisor排序问题
关于spring aop Advisor排序问题 当我们使用多个Advisor的时候有时候需要排序,这时候可以用注解org.springframework.core.annotation.Order或 ...
- 利用spring aop统一处理异常和打日志
利用spring aop统一处理异常和打日志 spring aop的概念,很早就写博客介绍了,现在在工作中真正使用. 我们很容易写出的代码 我们很容易写出带有很多try catch 和 logger. ...
- 我所理解的Spring AOP的基本概念
Spring AOP中的概念晦涩难懂,读官方文档更是像读天书,看了很多例子后,写一些自己理解的一些spring的概念.要理解面向切面编程,要首先理解代理模式和动态代理模式. 假设一个OA系统中的一个功 ...
最新文章
- MVP Summit 2008 照片纪实(二)- 旧金山,Google总部和Stanford大学
- 刚出道的黑客搞瘫美国!他们一边搞钱一边捐款给慈善组织,还说俄国人不打俄国人...
- 二维数组||考试成绩统计: 案例描述:有三名同学(张三,李四,王五),在一次考试中的成绩分别如下表,请分别输出三名同学的总成绩
- hdu-3033-I love sneakers!--背包
- 北大计算机大神,北大数学大神有多强?简直叹为观止
- python模拟键盘输入视频_python教程-模拟鼠标和键盘输入
- [蓝桥杯2015决赛]积分之迷-枚举(水题)
- P4302-[SCOI2003]字符串折叠【区间dp】
- 个人求职简历计算机应用技术学院信息门户,计算机应用技术专业求职简历范文...
- 我的在win10下安装tensorflow的过程
- 如何找出光纤微米级别的脏污?女朋友的一个举动给了我灵感
- 阿里巴巴宣布架构调整:集中发力推进三大战略 程立任集团CTO
- UWP ListView 绑定 单击 选中项 颜色
- cnsul linux环境后台启动_Linux环境下批量启动、停止或重启jar服务的shell脚本
- java——ArrayList中contains()方法中的疑问
- Windows 完成端口编程
- 056 CSRF跨站请求伪造
- 图像标签制作工具之labelImg-windows的安装与使用
- PADS VX2.8 正负片的概念以及设置方法
- 【应用统计学】总体均值的假设检验
热门文章
- python接口测试上传文件_python https 接口测试 上传文件
- 视图插入数据_带切片器的数据透视图
- oracle取_后的数字,聊聊四种Oracle数字取整函数
- @RestController和@Controller区别
- 如何更好地利用“二八定律”提升SEO优化效果?
- 网络营销助力之下国内可穿戴设备市场进一步打开迎来发展机遇
- LINUx设置ip导致内核挂死,Linux之TCPIP内核参数优化
- list和map用法java,java 集合list和map的用法
- 升级php7_PHP5.9 升级到PHP7 遇到的一些坑(phpfpm 图解)
- android隐藏状态栏