一、切面的几种示例:
Spring AOP 其实就是使用动态代理来对切面层进行统一的处理。
动态代理的方式有:JDK动态代理和 cglib 动态代理,
JDK 动态代理:基于接口实现,要求目标类必须实现接口。
cglib 动态代理:用第三方的工具库创建代理对象。基于子类实现。原理是继承。通过继承目标类,创建子类,子类就是代理对象。要求目标类不能是final的,方法也不能是final的。
spring默认使用的是JDK动态代理,如果没有接口,spring会自动的使用cglib动态代理。
注:有一点非常重要,Spring的AOP只能支持到方法级别的切入。换句话说,切入点只能是某个方法。
1)@Before--前置切面
@Aspect
@Component
public class MyAspect {
@Before("execution(* com.baizhi.service.*.*(..))")
public void before(JoinPoint joinPoint){
System.out.println("前置通知");
joinPoint.getTarget();//目标对象
joinPoint.getSignature();//方法签名
joinPoint.getArgs();//方法参数
}
}
2)@AfterThrowing
3)@After--后置切面
@Aspect
@Component
public class MyAspect {
@After("execution(* com.baizhi.service.*.*(..))")
public void before(JoinPoint joinPoint){
System.out.println("后置通知");
joinPoint.getTarget();//目标对象
joinPoint.getSignature();//方法签名
joinPoint.getArgs();//方法参数
}
}
4)@AfterReturning
5)@Around--环绕切面。有返回值,
@Aspect
@Component
public class MyAspect {
@Around("execution(* com.baizhi.service.*.*(..))")
public Object before(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("进入环绕通知");
proceedingJoinPoint.getTarget();//目标对象
proceedingJoinPoint.getSignature();//方法签名
proceedingJoinPoint.getArgs();//方法参数
Object proceed = proceedingJoinPoint.proceed();//放行执行目标方法
System.out.println("目标方法执行之后回到环绕通知");
return proceed;//返回目标方法返回值
}
}
注意: 前置通知和后置通知都没有返回值,方法参数都为joinpoint
注意: 环绕通知存在返回值,参数为ProceedingJoinPoint,如果执行放行,不会执行目标方法,一旦放行必须将目标方法的返回值返回,否则调用者无法接受返回数据
二、JointPoint和ProceedingJoinPoint区别
每个切面类中的通知方法都会绑定一个切入点,这个切入点在执行时会被切面类的通知方法在所定义的时间点拦截,此时将优先执行该通知内的逻辑。而这个切入点对应的方法将会把一些信息传入JointPoint中。
1、JointPoint包含以下属性:
# 返回目标对象,即被代理的对象。(切入点方法所在的类)
Object getTarget();
# 返回切入点的参数(方法上的形参)
Object[] getArgs();
# 返回切入点的Signature
Signature getSignature();
# 返回切入的类型,比如method-call,field-get等等,使用比较少
String getKind();
2、ProceedingJoinPoint继承了 JoinPoint。
是在JoinPoint的基础上暴露出 proceed 这个方法。ProceedingJoinPoint 用于环绕通知。
3、通过切入点操作目标对象(切入点所在类)
//--获取目标对象,即被代理的对象。(切入点所在的类)
Object targetObj =joinPoint.getTarget();
//--1、可以发挥反射的功能获取关于类的任何信息,例如获取类名如下
String className = joinPoint.getTarget().getClass().getName();
//--2、获取目标对象方法名,(切入点的方法名)
String methodName = joinPoint.getSignature().getName();
4、通过切入点获取目标对象,再通过反射获取注解信息
//切入点所在类(目标对象)
Object target = joinPoint.getTarget();
//切入点的方法名。
String methodName = joinPoint.getSignature().getName();
Method method = null;
//通过反射获取类中的方法
for (Method m : target.getClass().getMethods()) {
if (m.getName().equals(methodName)) {
method = m;
// xxxxxx annoObj= method.getAnnotation(xxxxxx.class);获取注解
break;
}
}
5、通过joinPoint.getSignature获取切入点的方法
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();//切入点的方法
if (method != null)
{
xxxxxx annoObj= method.getAnnotation(xxxxxx.class);
}
return null;
6、获取切入点方法的参数
Object[] args = joinPoint.getArgs();//返回是一个数组,参数列表
三、完整示例
切面一般时配合自定义注解使用。以下示例仅为简单使用。
1、引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
1、创建一个接口
@RequestMapping("/add")
public String addController(){
//我是一个接口
System.out.println("xxx在增加");
System.out.println("增加成功");
return "yes";
}
2、创建切面类
package com.xhy.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
//标识为切面类
@Aspect
//类不被识别,将类变成一个组件
@Component
@Slf4j
public class LogAop {
// 指定切入的规则,".."代表可有参可无参
@Pointcut("execution(* com.lv.controller.*Controller.*(..))")
public void logger(){}
// 环绕通知
@Around("logger()")
public void around(ProceedingJoinPoint point){
//执行逻辑
}
}