1.AOP的理解
1).名称:面向切面编程(Aspect Oriented Programming)
2).作用:降低系统中代码的耦合性,提高程序的可重用性,同时提高了开发的效率。
3).运行原理:是通过预编译的方法和运行期间动态代理实现程序功能的统一维护的一种技术,是spring框架中的一个重要内容
2.AOP的业务逻辑
AOP的主要意图:将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑中抽离出来,通过对于这些业务行为的分离,我们希望可以将他们独立到非指导业务逻辑的方法中,使我们可以根据不同的需求去改变,进而达到改变这些行为的时候不会影响到业务逻辑的代码。
3.通知类型
1.前置通知:@Before(“切点表达式”) 目标方法执行之前执行
2.后置通知:@AfterReturning(“切点表达式”) 目标方法执行之后执行
3.异常通知:@AfterThrowing(“切点表达式”) 目标方法执行过程中抛出异常时执行
4.最终通知:@After(“切点表达式”) 无论什么时候都要执行的通知
特点:以上四大通知类型无法干预目标方法是否执行,只是用来做程序运行状态的记录/监控
5.环绕通知:@Around(“切点表达式”) 在目标方法执行前后都要执行的通知方法 该方法可以控制目标是否允许 .joinPoint.proceed(); 功能最为强大
4.切入点表达式
理解:切入点表达式就是如何判断一个程序是否进入通知,相当如一个门卫,只有身份信息对于才能进入
作用:当程序运行过程中,满足了切点表达式时才会去执行通知方法,可以实现业务的拓展
种类(写法):
1.bean(bean的名称 bean的ID)只能拦截具体的某个bean对象 只能匹配一个对象
注:由于在springboot中会通过注释将一些类交给spring处理,此时的bean的id为类名的首字母小写
例:bean(“itemServiceImpl”)表示拦截类名为ItemServiceImpl的类
2.within(包名.类名) 例:within(“com,jt.service.*”) 可以匹配多个对象 粗粒度的匹配原则 按类匹配
3. execution(返回值类型 包名.类名.方法名(参数列表)) 最为强大的用法
注:execution(* com.jt.service…* .*(…))
返回值类型任意 com.jt.service包下所有的类的所有方法都会被拦截
4. @annotation(包名.注解名称) 按照注解匹配,那一方法或类上有注解,则会被AOP拦截下来
5.简述粗粒度与细粒度
粗粒度与细粒度的区别主要是出于重用的目的,像类的设计,为尽可能重用,所以采用细粒度的设计模式,将一个复杂的类(粗粒度)拆分成高度重用的职责清晰的类(细粒度)
6.连接点JoinPoint
理解:程序执行的某个特定位置(如:某个方法调用前、调用后,方法抛出异常后)。一个类或一段程序代码拥有一些具有边界性质的特定点,这些代码中的特定点就是连接点。Spring仅支持方法的连接点。
JoinPoint中API的应用:获取目标方法的类的全路径:joinPoint.getSignature().getDeclaringTypeName();
获取目标的方法名:joinPoint.getSignature().getName();
获取目标对象的类型:joinPoint.getTarget().getClass();
获取传递的参数:joinPoint.getArgs();
7.AOP入门
package com.jt.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Aspect //告诉这是一个AOP的切面类
@Component //表示将类交给spring容器管理
public class CacheAop {
//公式 = 切入点表达式 + 通知方法
/**
* 关于切入点表达式的使用说明
* 粗粒度:
* 1.bean (bean的Id) 一个类
* 2.within(包名,类名) 多个类
* 细粒度
*/
//定义切入点表达式,只为了占位
// @Pointcut("bean(itemCatServiceImpl)")
//@Pointcut("within(com.jt.service..*)") //匹配多级目录 在service类下的包通通执行该AOP
@Pointcut("execution(* com.jt.service..*.*(..))") //方法参数级别
public void pointCut(){
}
/**
* 需求:获取目标对象的相关信息
* 1.获取目标方法的路径 包名.类名.方法名
* 2.获取目标方法的类型 class
* 3.获取传递的参数
* 4.记录当前的执行时间
*/
//定义前置通知,与切入点表达式进行绑定,注意绑定的是方法
//两种写法的区别: pointCut() 表示切入点表达式的引用,适用于多个通知时用切入点的情况
// @Before("bean(itemCatServiceImpl)") 适用于单个通知,不需要重复
@Before("pointCut()")
public void before(JoinPoint joinPoint){ //JoinPoint 连接点
String className = joinPoint.getSignature().getDeclaringTypeName();
String methodName = joinPoint.getSignature().getName();
Class targetClass = joinPoint.getTarget().getClass();
Object[] args = joinPoint.getArgs();
long runTime = System.currentTimeMillis();
System.out.println("方法全路径"+className+"."+methodName);
System.out.println("目标对象类型:"+targetClass);
System.out.println("参数:"+ Arrays.toString(args));
System.out.println("执行时间:"+runTime+"毫秒");
}
/**
* 环绕通知说明
* 注意事项:
* 1.环绕通知中必须添加参数ProceedingJoinPoint
* 2.ProceedingJoinPoint只能在环绕通知中使用
* 3.proceedingJoinPoint如果当作参数 则必须位于参数的第一位
*/
@Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint){
System.out.println("环绕通知开始");
Object result = null;
try {
result=joinPoint.proceed(); //执行下一个通知或者目标方法
}catch (Throwable throwable){
throwable.printStackTrace();
}
System.out.println("环绕通知结束");
return result;
}
//定义后置通知
@AfterReturning("pointCut()")
public void afterReturn(){
System.out.println("我是后置通知");
}
//定义最终通知
@After("pointCut()")
public void after(){
System.out.println("我是最终通知");
}
}