AOP 1,意为:面向切面编程,经过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。基于AOP实现的功能不会破坏原来程序逻辑,所以它能够很好的对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度下降,提升程序的可重用性,同时提升了开发的效率。css
pom.xml
中加入 aop 依赖:<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
CGLIB
来实现 AOP,在系统配置文件中加入设置:spring:
aop:
proxy-target-class: true
默认为 false
。java
列出经常使用的几个表达式:
1. execution()
知足execution中描述的方法签名。 execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?)
* modifier-pattern
:表示方法的修饰符;
* ret-type-pattern
:表示方法的返回值
* declaring-type-pattern
:表示方法所在的类的路径
* name-pattern
:表示方法名
* param-pattern
:表示方法的参数
* throws-pattern
:表示方法抛出的异常
* 其中后面跟着?
的是可选项。spring
this()
是用来限定方法所属的类,为接口则限定全部的实现类,为类的话,限定这单个类。@annotation
表示具备某个标注的方法。args
表示方法的参数属于一个特定的类,@args
表示参数有特定的标注注解。within
包或者类型知足within中描述的包或者类型的类的全部非私有方法,@within
类型拥有@target描述中给出的annotation,其中@target和@within的区别在于@within要求的annotation的级别为CLASS,而@target为RUNTIME target
业务实例对象(非代理实例)的类型知足target 中的描述的类型,@target 类型拥有@target描述中给出的annotationbean()
表示全部匹配的 bean,例如 ,bean(“*Service”),匹配全部 Service
结尾的类。可使用 !bean()
表示不匹配。注意事项:
* 在各个pattern中,可使用”*”来表示匹配全部。
* 在param-pattern
中,能够指定具体的参数类型,多个参数间用,
隔开,各个也能够用 *
来表示匹配任意类型的参数,如(String)
表示匹配一个String
参数的方法;(*,String)
表示匹配有两个参数的方法,第一个参数能够是任意类型,而第二个参数是String类型。
* 能够用(..)
表示零个或多个任意的方法参数。
* 使用&&
符号表示与关系,使用||
表示或关系、使用!
表示非关系。在XML文件中使用and
、or
和not
这三个符号。编程
AspectJ提供了五种定义通知的标注:springboot
@Before
:前置通知,在调用目标方法以前执行通知定义的任务@After
:后置通知,在目标方法执行结束后,不管执行结果如何都执行通知定义的任务@AfterReturning
:后置通知,在目标方法执行结束后,若是执行成功,则执行通知定义的任务@AfterThrowing
:异常通知,若是目标方法执行过程当中抛出异常,则执行通知定义的任务@Around
:环绕通知,在目标方法执行前和执行后,都须要执行通知定义的任务经过标注定义通知只须要两个步骤:
1. 将以上五种标注之一添加到切面的方法中
2. 在标注中设置切点的定义。markdown
环绕通知相比其它四种通知有其特殊之处。环绕通知本质上是将前置通知、后置通知和异常通知整合成一个单独的通知。函数
用@Around
标注的方法,该方法必须有一个ProceedingJoinPoint
类型的参数,spring-boot
在方法体中,须要经过这个参数,以joinPoint.proceed();
的形式调用目标方法。注意在环绕通知中必须进行该调用,不然目标方法自己的执行就会被跳过。this
计算方法的执行时间:spa
@Around("logPointCut()") //切点
public Object around(ProceedingJoinPoint point) throws Throwable {
long beginTime = System.currentTimeMillis();
//执行方法
Object result = point.proceed();
//执行时长(毫秒)
long time = System.currentTimeMillis() - beginTime;
return result;
}
获取参数的方式则须要使用关键词是args
。
@Pointcut("bean(sysUserServiceImpl) && args(userEntity,..)")
public void userPointCut(SysUserEntity userEntity) {
}
@Before("userPointCut(userEntity)")
public void validateUser(SysUserEntity userEntity) {
// to handler args
}
这里有个很是严格的一点就是,args(userEntity,..)
,表示目标方法,可能有多个参数,可是包括 userEntity
,这里 userEntity
必须参数名相同,不一样就编织了。
args()
中参数的名称必须跟切点方法的签名中public void validateUser(SysUserEntity userEntity)
的参数名称相同。若是使用切点函数定义,其中的参数名称也必须与通知方法签名中的参数彻底相同
@AfterReturning(pointcut = "logPointCut()", returning = "rtv")
public void logAfter(Object rtv) {
System.out.println(Objects.toString(rtv));
}
使用 @AfterReturning
注解时,指定了一个returning
属性,假设该属性值为rvt
,这代表容许在Advice方法(logAfter()方法)中定义名为rvt的形参,程序可经过rvt形参来访问目标方法的返回值。
注意:
虽然AfterReturning
加强处理能够访问到方法的返回值,但它不能够改变目标方法的返回值。
有时候,咱们对一个方法会有多个切面的问题,这个时候还会涉及到切面的执行顺序的问题。
咱们能够定义每一个切面的优先级, Spring 中提供注解 @Order(i)
,当 i
的值越小,优先级越高。