Spring的AOP
1.AOP的引入(面向切面编程底层的实现就是通过动态代理实现的)
动态代理常用的有两种: JDk动态代理 和 CGLIB动态代理
如果目标类(是指的service实现类中的类)实现接口,采用的是JDK动态代理,如果没有实现接口,那么采用的是CGLIB动态代理
JDK动态代理:
首先创建目标对象,在创建代理对象,通过proxy类调用类中的静态方法 newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
ClassLoader loader 目标类加载器
Class<?>[] interfaces 目标类实现的所有的接口
InvocationHandler h 目标方法的调用处理器(在这里通过匿名内部类调用主业务逻辑,并且将将交叉的业务逻辑织入主业务逻辑中)
最后通过增强后代理对象来调用目标方法;
AOP 的原理:
每个 Bean 都会被 JDK 或者 Cglib 代理。取决于是否有接口。
每个 Bean 会有多个“方法拦截器”。注意:拦截器分为两层,外层由 Spring 内核控制流程,内层拦截器是用户设置,也就是 AOP。
当代理方法被调用时,先经过外层拦截器,外层拦截器根据方法的各种信息判断该方法应该执行哪些“内层拦截器”。内层拦截器的设计就是职责连的设计。
第一:代理的创建;
第二:代理的调用。
代理的创建(按步骤):
首先,需要创建代理工厂,代理工厂需要 3 个重要的信息:拦截器数组,目标对象接口数组,目标对象。
创建代理工厂时,默认会在拦截器数组尾部再增加一个默认拦截器 —— 用于最终的调用目标方法。
当调用 getProxy 方法的时候,会根据接口数量大余 0 条件返回一个代理对象(JDK or Cglib)。
注意:创建代理对象时,同时会创建一个外层拦截器,这个拦截器就是 Spring 内核的拦截器。用于控制整个 AOP 的流程。
代理的调用
当对代理对象进行调用时,就会触发外层拦截器。
外层拦截器根据代理配置信息,创建内层拦截器链。创建的过程中,会根据表达式判断当前拦截是否匹配这个拦截器。而这个拦截器链设计模式就是职责链模式。
当整个链条执行到最后时,就会触发创建代理时那个尾部的默认拦截器,从而调用目标方法。最后返回。
2.AOP的介绍(作用:就是将交叉业务逻辑和主业务逻辑解耦)
(IOC作用:将主业务逻辑层与层解耦)
面向切面编程,就是将交叉业务逻辑(就是与书业务逻辑无关的代码,例如事物/日志…)封装成一个切面(切面就相当于一个类),然后利用Spring容器的功能将切面织入主业务逻辑中.
优点: 使用AOP,使交叉业务逻辑和主业务逻辑进行解耦,提高代码的复用性,以及方便代码的编写和后期的维护
3.通知(Advice)和顾问(Advisor)都是切面实现的一种;
(1)通知: 可以完成一些简单的织入到目标对象中 顾问: 能完成更为复杂一些织入
(2)通知: 是不可以指定切入点的 顾问: 是可以指定具体切入点
4.常用通知分类
前置通知(MethodBeforeAdvice)
后置通知(AfterReturningAdvice)
环绕通知(MethodInterceptor)
异常处理通知(ThrowsAdvice)
5.通知的用法步骤
6.1定义目标类
6.2定义通知类
6.3注册目标类
6.4注册通知切面
6.5注册代理工厂Bean类对象ProxyFactoryBean
6.6客户端访问动态代理对象
6.AspectJ(是一个框架)对AOP 的实现
对于AOP这种思想,许多框架都进行了实现;其中Spring就是实现的其中之一,完成了面向切面编程,然而AspectJ也实现了AOP的功能,且其实现的方式更为简捷,方便,而且支持注解式开发;所以Spring框架将AspectJ对AOP的实现引用到自己的框架中,在Spring中使用AOP开发时,通常是用AspectJ进行实现
7.AspectJ的通知类型:
前置通知
后置通知
环绕通知
异常处理通知
最终通知:无论程序执行是否正常,最终通知都会执行
8.AspectJ对于AOP的实现有两种:
(1)注解方式
对应的注解: @Aspect/@Before/@AfterReturning/@Around/@AfterThrowing/@After
2.2 Advice的主要类型
@Before:该注解标注的方法在业务模块代码执行之前执行,其不能阻止业务模块的执行,除非抛出异常;
@AfterReturning:该注解标注的方法在业务模块代码执行之后执行;
@AfterThrowing:该注解标注的方法在业务模块抛出指定异常后执行;
@After:该注解标注的方法在所有的Advice执行完成后执行,无论业务模块是否抛出异常,类似于finally的作用;
@Around:该注解功能最为强大,其所标注的方法用于编写包裹业务模块执行的代码,其可以传入一个ProceedingJoinPoint用于调用业务模块的代码,
无论是调用前逻辑还是调用后逻辑,都可以在该方法中编写,甚至其可以根据一定的条件而阻断业务模块的调用;
@DeclareParents:其是一种Introduction类型的模型,在属性声明上使用,主要用于为指定的业务模块添加新的接口和相应的实现。
@Aspect:严格来说,其不属于一种Advice,该注解主要用在类声明上,指明当前类是一个组织了切面逻辑的类,并且该注解中可以指定当前类是何种实例化方式,
主要有三种:singleton、perthis和pertarget,具体的使用方式后面会进行讲解。
这里需要说明的是,@Before是业务逻辑执行前执行,与其对应的是@AfterReturning,而不是@After,@After是所有的切面逻辑执行完之后才会执行,
无论是否抛出异常。
@Pointcut 提取切入点表达式
本文主要介绍spring aop中9种切入点表达式的写法
execute:可以用于明确指定方法返回类型,类名,方法名和参数名等与方法相关的部件
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
这里问号表示当前项可以有也可以没有,其中各项的语义如下:
modifiers-pattern:方法的可见性,如public,protected;
ret-type-pattern:方法的返回值类型,如int,void等;
declaring-type-pattern:方法所在类的全路径名,如com.spring.Aspect;
name-pattern:方法名类型,如buisinessService();
param-pattern:方法的参数类型,如java.lang.String;
throws-pattern:方法抛出的异常类型,如java.lang.Exception;
如下是一个使用execution表达式的例子:
execution(public * com.spring.service.BusinessObject.businessService(java.lang.String,…)) 注:* 、… 都是通配符。
within: 表达式的粒度为类,其参数为全路径的类名(可使用通配符),表示匹配当前表达式的所有类都将被当前方法环绕。如下是within表达式的语法:
within(declaring-type-pattern)
this
target
this和target表达式中都只能指定类或者接口
args表达式的作用是匹配指定参数类型和指定参数数量的方法,无论其类路径或者是方法名是什么。这里需要注意的是,args指定的参数必须是全路径的。
如下是args表达式的语法:args(param-pattern)
@target
@within
@annotation
@args