Spring中通过切入点表达式定义具体切入点,其常用AOP切入点表达式定义及说明:
表-1 Spring AOP 中切入点表达式说明
指示符 作用
bean
用于匹配指定bean对象的所有方法
within
用于匹配指定包下所有类内的所有方法
execution
用于按指定语法规则匹配到具体方法
@annotation
用于匹配指定注解修饰的方法
bean表达式一般应用于类级别,实现粗粒度的切入点定义,案例分析:
bean(“userServiceImpl”)指定一个userServiceImpl类中所有方法。
bean("*ServiceImpl")指定所有后缀为ServiceImpl的类中所有方法。
说明:bean表达式内部的对象是由spring容器管理的一个bean对象,表达式内部的名字应该是spring容器中某个bean的name。
within表达式应用于类级别,实现粗粒度的切入点表达式定义,案例分析:
within(“aop.service.UserServiceImpl”)指定当前包中这个类内部的所有方法。
within(“aop.service.") 指定当前目录下的所有类的所有方法。 within("aop.service…”)
指定当前目录以及子目录中类的所有方法。 within表达式应用场景分析:
1)对所有业务bean都要进行功能增强,但是bean名字又没有规则。 2)按业务模块(不同包下的业务)对bean对象进行业务功能增强。
execution表达式应用于方法级别,实现细粒度的切入点表达式定义,案例分析: 语法:execution(返回值类型
包名.类名.方法名(参数列表))。 execution(void
aop.service.UserServiceImpl.addUser())匹配addUser方法。 execution(void
aop.service.PersonServiceImpl.addUser(String))
方法参数必须为String的addUser方法。 execution(* aop.service….(…)) 万能配置。
@annotaion表达式应用于方法级别,实现细粒度的切入点表达式定义,案例分析 @annotation(anno.RequiredLog)
匹配有此注解描述的方法。 @annotation(anno.RequiredCache) 匹配有此注解描述的方法。
其中:RequiredLog为我们自己定义的注解,当我们使用@RequiredLog注解修饰业务层方法时,系统底层会在执行此方法时进行日扩展操作。
示例:
@Order(1)
@Slf4j
@Component
@Aspect
// aspect 注解描述的类型为spring AOP中的切面类型
public class SysLogAspect {
@Autowired
private SysLogService logService;
// 切入点
/**
* bean 为 切入点类型
* sysUserServiceImpl 为spring容器中的bean
* <p>
* 这里的含义是 当sysUserServiceImpl 中的每一个方法执行时 都由本
* 切面对象的通知方法做功能增强
*/
//@Pointcut("bean(sysUserServiceImpl)")
// 使用自定义注解作为切入点
@Pointcut("@annotation(com.cy.pj.common.annotation.RequiredLog)")
public void doLogPointcut() {
}
;
/**
* 环绕通知 相对于其他的通知 优先级最高
*
* @param joinPoint 为一个连接对象 封装了正在要执行的目标方法
* @return 目标方法的执行结果
* @throws Throwable
*/
@Around("doLogPointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//log.info("method SysLog start {}",df.format(new Date()));
try {
long startTime = System.currentTimeMillis();
Object result = joinPoint.proceed(); // 最终会执行的方法
long endTime = System.currentTimeMillis();
long time = endTime - startTime;
// 将用户的操作日志写入数据库
saveLog(joinPoint, time);
//log.info("method SysLog end{}",df.format(new Date()));
return result;
} catch (Throwable e) {
log.error("method SysLog error {},{}", df.format(new Date()), e.getMessage());
throw e;
}
}
// 将用户的操作日志写入数据库
private void saveLog(ProceedingJoinPoint joinPoint, Long time) throws Exception {
// 1.获取用户行为信息
// 目标对象类型
Class<?> jpCls = joinPoint.getTarget().getClass();
// 获取方法签名对象(这个方法封装了要执行的目标方法信息)
MethodSignature ms = (MethodSignature) joinPoint.getSignature();
String MethodName = jpCls.getName() + "." + ms.getName();
// 获取目标方法对象 ,基于此对象获取方法上的RequiredLog注解,进而去到注解的值
Method method = jpCls.getMethod(ms.getName(), ms.getParameterTypes());
RequiredLog requiredLog = method.getAnnotation(RequiredLog.class);
String annotationValue = requiredLog.value();
// 2.构建用户对象 入库
SysLog log = new SysLog();
log.setIp(IPUtils.getIpAddr()); // 本机ip
log.setMethod(MethodName); // 类名 + 方法名
log.setOperation(annotationValue); // 操作
log.setParams(Arrays.toString(joinPoint.getArgs())); // 参数
log.setTime(time); // 执行时长
log.setCreatedTime(new Date()); // 创建时间
log.setUsername("admin"); // 操作用户
logService.saveObject(log); // 入库
}
}