横切、通知、连接点、切入点、切面???
横切关注点:
- 对哪些方法进行拦截,拦截后怎么处理,这些就叫横切关注点
- 比如 权限认证、日志、事物
通知 Advice:
所谓通知是指拦截到Joinpoint之后所要做的事情就是通知。
通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
-
@Before前置通知
- 在执行目标方法之前运行
-
@After后置通知
- 在目标方法运行结束之后
-
@AfterReturning返回通知
- 在目标方法正常返回值后运行
-
@AfterThrowing异常通知
- 在目标方法出现异常后运行
-
@Around环绕通知
- 在目标方法完成前、后做增强处理 ,环绕通知是最重要的通知类型 ,像事务,日志等都是环绕通知,注意编程中核心是一个ProceedingJoinPoint,需要手动执行 joinPoint.procced()
连接点 JointPoint:
所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点。(通俗理解:业务层接口的所有方法都叫连接点)
切入点 Pointcut:
所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。 (通俗理解:被增强的业务层接口的方法叫切入点)
(这样看来,连接点不一定是切入点,但切入点一定是连接点。)
切面 Aspect
- 通常是一个类,里面定义 切入点+通知 , 定义在什么地方; 什么时间点、做什么事情
- 通知 advice指明了时间和做的事情(前置、后置等)
- 切入点 pointcut 指定在什么地方干这个事情
- web接口设计中,web层->网关层->服务层->数据层,每一层之间也是一个切面,对象和对象,方法和方法之间都是一个个切面
目标 target
- 目标类,真正的业务逻辑,可以在目标类不知情的条件下,增加新的功能到目标类的链路上
织入 Weaving
- 把切面(某个类)应用到目标函数的过程称为织入
废话少说上代码
//目标类 VideoOrderService; 里面每个方法都是连接点,;切入点是CUD类型的方法,R读取的不作为切入点
//CRDU全称:增加(Create)、读取查询(Retrieve)、更新(Update)和删除(Delete)
public class VideoOrderService{
//新增订单
addOrder(){ }
//查询订单
findOrderById(){}
//删除订单
delOrder(){}
//更新订单
updateOrder(){}
}
//权限切面类 = 切入点+通知
public class PermissionAspects{
//切入点 定义了什么地方
@Pointcut("execution(public int net.xdclass.sp.service.VideoOrderService.*(..))")
public void pointCut(){}
//before 通知 表示在目标方法执行前切入, 并指定在哪个方法前切入
//什么时候,做什么事情
@Before("pointCut()")
public void permissionCheck(){
System.out.println("在 xxx 之前执行权限校验");
}
....
}
//日志切面类 = 切入点+通知
public class LogAspect{
//切入点 定义了什么地方
@Pointcut("execution(public int net.xdclass.sp.service.VideoOrderService.*(..))")
public void pointCut(){}
//after 通知 表示在目标方法执行后切入, 并指定在哪个方法前切入
//什么时候,做什么事情
@After("pointCut()")
public void logStart(){
System.out.println("在 xxx 之后记录日志");
}
....
}
切入点表示式
- 除了返回类型、方法名和参数外,其它项都是可选的 (修饰符基本都是省略不写)
访问修饰符 返回值类型(必填) 包和类 方法(必填)
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
-
- @Pointcut("execution(public int net.xdclass.sp.service.VideoOrderService.*(..))")
-
常见匹配语法
-
*:匹配任何数量字符 单个;
-
..:匹配任何数量字符,可以多个,在类型模式中匹配任何数量子包;在方法参数模式中匹配任何数量参数
-
() 匹配一个不接受任何参数的方法
(..) 匹配一个接受任意数量参数的方法
(*) 匹配了一个接受一个任何类型的参数的方法
(*,Integer) 匹配了一个接受两个参数的方法,其中第一个参数是任意类型,第二个参数必须是Integer类型
常见例子
-
任意公共方法
execution(public * *(..))
-
任何一个名字以“save”开始的方法
execution(* save*(..))
-
VideoService接口定义的任意方法(识别)
execution(* net.xdclass.service.VideoService.*(..))
-
在service包中定义的任意方法(识别)
execution(* net.xdclass.service.*.*(..))
-
匹配 service 包,子孙包下所有类的所有方法(识别)
execution(* net.xdclass.service..*.*(..))
注解实战
@Component
//告诉spring,这个一个切面类,里面可以定义切入点和通知
@Aspect
public class LogAdvice {
//切入点表达式
@Pointcut("execution(* net.xdclass.sp.service.VideoServiceImpl.*(..))")
public void aspect(){
}
//前置通知
@Before("aspect()")
public void beforeLog(JoinPoint joinPoint){
System.out.println("LogAdvice beforeLog");
}
//后置通知
@After("aspect()")
public void afterLog(JoinPoint joinPoint){
System.out.println("LogAdvice afterLog");
}
}
@Component
//告诉spring,这个一个切面类,里面可以定义切入点和通知
@Aspect
public class LogAdvice {
//切入点表达式,也可以直接在通知上编写切入点表达式
@Pointcut("execution(* net.xdclass.sp.service.VideoServiceImpl.*(..))")
public void aspect(){
}
//前置通知
//@Before("aspect()")
@Before("execution(* net.xdclass.sp.service.VideoServiceImpl.*(..))")
public void beforeLog(JoinPoint joinPoint){
System.out.println("LogAdvice beforeLog");
}
//后置通知
@After("aspect()")
public void afterLog(JoinPoint joinPoint){
System.out.println("LogAdvice afterLog");
}
这段代码啥意思呢?切入点定义在
@Pointcut("execution(* net.xdclass.sp.service.VideoServiceImpl.*(..))")
视频接口的实现类下的方法 在这个方法执行之前后 先执行
beforeLog()和after()这个俩方法 这个就类似登录拦截器那个一样的思想 不得不说研究出AOP的大佬是真的厉害。
方法的参数是连接点 就是每个方法都可以作为连接点 打印需要的日志。
注意
环绕通知=前置+目标方法执行+后置通知,