Spring系列(七)
系列目录
AOP(二)SpringAOP详解
1. 基本概念
-
连接点(Joinpoint):在程序执行过程中某个特定的点,如某方法调用的时候或者处理异常的时候。
在SpringAOP中,一个连接点总是表示一个方法的执行。通俗的讲,层与层之间方法的调用过程称之为连接点。 -
切入点(Pointcut):匹配连接点的断言。通过和一个切入点表达式关联,并在满足这个切入点的连接点上
运行,(例如,当执行某个特定名称的方法时)。
切入点表达式如何和连接点匹配是AOP的核心:Spring缺省使用Aspect切入点语法。通俗的讲:在连接点的基础上增加切入规则,选择出需要进行增强的连接点。这些基于切入规则选出来的连接点就称之为切入点。 -
切面(Aspect):一个关注点的模块化,这个关注点可能会横切出多个对象,如J2EE中事务管理的应用。
在Spring AOP中,切面可以使用基于模式或者基于@Aspect注解来实现。
狭义的理解,当spring拦截下切入点后,将这些切入点交给处理类进行功能的增强,这个处理类就称之为切面。
广义的理解,将spring底层的代理、切入点和处理类加在一起实现的对层与层之见调用过程进行增强的机制称之为切面。 -
通知(Advice):在切面的某个特定的连接点上执行的动作。
其中包括了“around”、“before”、“after”等不同类型的通知。许多AOP框架,包括Spring都是以拦截器做通知模型,并维护一个以连接点为中心的拦截器链。
在spring底层的代理拦截下切入点后,将切入点交给切面类,切面类中需要有处理这些切入点的方法,即称之为
通知,也叫增强或增强方法。针对于切入点执行的过程,通知可以分为不同的类型,分别关注切入点在执行过程中的不同的时机。 -
目标对象(Target Obejct):被一个或者多个切面所通知的对象,也被称之为被通知(advised)对象。
SpringAOP是通过运行时代理实现的,这个对象永远是一个被代理(proxied)对象。通俗的讲,目标对象就是真正希望被访问到的对象。spring底层的动态代理对他进行了代理,具体能不能真的访问到目标对象,或在目标对象真正执行之前和之后是否做一些额外的操作,取决于切面。
-
织入(Swaving):用以表述将某个增强功能加入到目标对象。
2. 切入点表达式
-
with表达式
通过类名进行匹配,是一种粗粒度的切入点表达式,会将这个类中的所有连接点进行表达式识别,成为切入点。
with(包名.类名)
-
execution表达式
细粒度的切入点表达式,可以以方法为单位定义切入点规则。
execution(返回值类型 包名.类名.方法名(参数类型,参数类型))
3. Spring的五大通知类型
-
前置通知:在目标方法执行之前执行的通知。
前置通知方法,可以没有参数,也可以额外接受一个JoinPoint,Spring会自动将该对象传入,代表当前的连接点,通过该对象可以获取目标对象和目标方法相关的信息。如果接收JoinPoint,必须保证其为方法参数列表的第一个参数,否则报错。 -
环绕通知:在目标方法执行之前和执行之后都可以执行额外代码的通知。
在环绕通知中,必须显示的调用目标方法,否则目标方法不会执行。此调用通过ProceedingJoinPoint实现,可以在环绕通知中接收一个此类型的形参,spring容器会自动将该对象传入,这个参数必须处在环绕通知的第一个形参位置。
注意:- ProceedingJoinPoint时JoinPoint的子类,只有环绕通知可以接收ProceedingJoinPoint,而其他通知只能接收JoinPoint。
- 环绕通知需要返回返回值,否则真正调用者将拿不到返回值,只能得到一个null。
- 用处:
- 控制目标方法是否执行。
- 目标方法执行之前或之后执行额外代码。
- 控制是否返回返回值。
- 改变返回值。
-
后置通知:在目标方法执行成功之后执行的通知。
在后置通知中也可以选择性的接收一个JoinPoint来获取连接点的额外信息,但是这个参数必须处在参数列表的第一个。
在后置通知中,还可以通过配置获取目标方法的返回值。 -
异常通知:在目标方法抛出异常时执行的通知。
可以配置传入JoinPoint获取目标对象和目标方法相关信息,但必须处在参数列表第一位。另外,还可以配置参数,让异常通知接收到目标方法抛出的异常对象 -
最终通知:目标方法执行之后执行的通知。
和后置通知不同之处在于,后置通知是在方法正常返回后执行的通知,如果方法没有正常返-例如抛出异常,则后置通知不会执行。
而最终通知无论如何都会在目标方法调用过后执行,即使目标方法没有正常的执行完成。另外,后置通知可以通过配置得到返回值,而最终通知无法得到。
4. 五类通知执行的顺序
- 在目标方法没有抛出异常的情况下
- 前置通知
- 环绕通知的调用目标方法之前的代码(取决于配置顺序)
- 目标方法
- 环绕通知的调用目标方法之后的代码
- 后置通知
- 最终通知(取决于配置顺序)
- 在目标方法抛出异常的情况下
- 前置通知
- 环绕通知的调用目标方法之前的代码(取决于配置顺序)
- 目标方法 抛出异常
- 异常通知
- 最终通知(取决于配置顺序)
- 如果存在多个切面
多个切面执行时,采用了责任链设计模式,切面的配置顺序决定了切面的执行顺序,执行过程类似于方法调用的过程。在环绕统治的proceed()执行时,去执行下一个切面。
5. 五种通知的常见使用场景
- 前置通知:记录日志(方法将被调用)
- 环绕通知:控制事务 权限控制
- 后置通知:记录日志(方法已经成功调用)
- 异常通知:异常处理 控制事务
- 最终通知:记录日志(方法已经调用但不一定成功)
6. SpringAOP原理
Spring在创建bean时,除了创建目标对象bean之外,会根据aop的配置,生产目标对象的代理对象,将其存储之后获取bean时得到的其实是代理对象,在代理对象中,会根据配置的切入点规则,决定哪些方法不处理,哪些方法拦截后增强,需要增强的方法拦截后根据配置调用指定切面中的指定通知执行增强操作。
Spring自动为目标对象生成代理对象,默认情况下如果目标对象实现过接口,则采用java的动态代理机制,如果目标对象没有实现过接口,则采取cglib动态代理。
可以在spring中配置,要求无论目标对象是否实现过接口,都强制使用cglib动态代理。