最近在项目中要记录用户日志,使用自定义注解进行aop拦截,发现可以拦截调用类的第一个方法,方法中调用其他方法,则其他方法无法拦截,经过查询,发现是aop自身的动态代理造成的,下面我贴出项目的代码:
1、自定义注解
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogController {
String operationContent() default "";
}
2、spring的aop代码
@Aspect
@Component
public class UserLogAspect {
@Pointcut("@annotation(com.jingli.creditchain.service.userlog.LogController)")
public void methodAspect() {
}
@SuppressWarnings("rawtypes")
@After("methodAspect()")
public void doAfter(JoinPoint joinPoint) {
try {
//拦截的实体类
Object target = joinPoint.getTarget();
//拦截的方法名称
String methodName = joinPoint.getSignature().getName();
//拦截的参数类型
Class[] parameterTypes = ((MethodSignature)joinPoint.getSignature()).getMethod().getParameterTypes();
//通过反射获得拦截的method
Method method = target.getClass().getMethod(methodName, parameterTypes);
String content = method.getAnnotation(LogController.class).operationContent();
//打印注解上的说明
System.out.println("================="+content);
} catch (Exception e) {
e.printStackTrace();
}
}
}
3、controller中进行方法拦截
@LogController(operationContent = "findApply")
@RequestMapping(path = "/list", method = RequestMethod.POST)
public List<ApplyVm> findApply(@RequestBody QueryInnerApplyVm query) {
//转换请求vm,在转换的方法上也加上拦截(转换方法在同一个类中)
QueryParam queryParam = queryInnerApplyVmToQueryParam(query);
return applyService.findApply(queryParam)
.stream()
.filter(Objects::nonNull)
.map(this::assignApplyVm)
.collect(Collectors.toList());
}
//转换方法
@LogController(operationContent = "queryInnerApplyVmToQueryParam")
public QueryParam queryInnerApplyVmToQueryParam(QueryInnerApplyVm vm) {
return new QueryParam();
}
4、测试,看打印结果
=================findApply
可以看到,方法中调用的第二个方法并没有拦截。
原因:
当调用这个类的时候,调用的是代理对象,代理类内部代码大致是:
private ApplyController applyController;
public List<ApplyVm> findApply(@RequestBody QueryInnerApplyVm query) {
//也就是说,在调用其他函数时,其实是被代理对象调用的,所以方法是不会被增强,也就是没
有拦截。调用其他函数时,如果是代理对象调用的,那就可以被拦截。
QueryParam queryParam = applyController.queryInnerApplyVmToQueryParam(query);
}
5、修改代码
拿到ApplicationContext,从容器中拿对象,如果那个对象有代理对象,就可以获得代理对象。
//工具类
@Component
public class BeanUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(@NotNull ApplicationContext applicationContext){
BeanUtil.applicationContext = applicationContext;
}
public static <T> T getBean(Class<T> bean){
return applicationContext.getBean(bean);
}
}
@LogController(operationContent = "findApply")
@RequestMapping(path = "/list", method = RequestMethod.POST)
public List<ApplyVm> findApply(@RequestBody QueryInnerApplyVm query) {
QueryParam queryParam = BeanUtil.getBean(this.getClass()).queryInnerApplyVmToQueryParam(query);
}
6、测试
=================queryInnerApplyVmToQueryParam
=================findApply
第二个方法也被拦截了。
7、总结
spring是不建议这种使用aop有函数内部调用的,可以把调用的函数写到另一个类中。但是这样的话,代码会有点乱,所以我项目中看情况如果上面的写法方便的话我还是用上面的方法把=。=
这个问题一开始困扰我好久,上网查查这问题有大神解答,十分感谢,也记录一下。