问题起因:
首先有块业务,需要添加埋点内容,但是此处埋点在入参值中有一个字段值,这个字段值的不同,会发送不同的打点信息(这个是在接口返回正确的情况下调用的),奔着解耦原业务和埋点的出发点,于是选择利用注解和SpringAOP来实现,其实这个实现还有其他的方式。但是此次就我实现的方式解释下,并把遇到的问题记录下。
二话不说,先上代码
@AfterReturning(value = "interceptorMethod(reqModel,request)", returning = "responseModel")
public void afterReturningAdvice(JoinPoint joinPoint, ReqPublishDynamicModelXX reqModel, HttpServletRequest request, ResponseModel responseModel) {
if (responseModel.getError() == ErrorCodes.OK){
Signature signature = joinPoint.getSignature();
if (signature instanceof MethodSignature) {
MethodSignature methodSignature = (MethodSignature) signature;
PointResource pointResource = methodSignature.getMethod().getAnnotation(PointResource.class);
if (null != pointResource) {
InvocationHandler handler = Proxy.getInvocationHandler(pointResource);
Field sourceField;
try {
sourceField = handler.getClass().getDeclaredField("memberValues");
sourceField.setAccessible(true);
Map<String, Object> memberValues = (Map<String, Object>) sourceField.get(handler);
if (reqModel.getTopicId() <= 0) {
memberValues.put("source", 7);
} else {
memberValues.put("source", 5);
}
} catch (Exception e) {
//省略.....
}
userPointService.handleUserPointResource(userModel.getUid(), pointResource.source(), pointResource.type(), pointResource.plus(), UUID.randomUUID().toString());
}
}
}
}
接下来针对代码中的部分内容进行解
1、释@AfterReturning这个是在方法执行返回后织入的,因为要根据业务的调用结果来判断是否需要执行,这点很简单。
2、PointResource pointResource = methodSignature.getMethod().getAnnotation(PointResource.class);这句是获取方法上的注释类,InvocationHandler handler = Proxy.getInvocationHandler(pointResource);这一句是利用代理来获取注解类的InvocationHandler,其实对应注解类的具体InvocationHandler接口实现类是AnnotationInvocationHandler,但是这个类不是public的,所以无法直接使用,知道具体的实现类之后,我们会从AnnotationInvocationHandler的源码类中发现所有的注解类的字段都维护在一个叫做Map<String, Object> memberValues这么个字段中,所有才有sourceField = handler.getClass().getDeclaredField("memberValues");这么一句,在执行完这一句之后,我们会获取到Map<String, Object> memberValues字段,并且通过 memberValues.put("source", 7);这样类似的语句来实现控制注解的字段值内容。
到这里所有的业务实现都已经完成了,接下来说说遇到的坑点.....
===================================华丽的分界线================================
坑点描述:加在方法上的注释类,由于当前类是由Spring托管的,所以对于该类的默认对象实例是单例类,那么对于加在该类方法上的注解类也是单例的(可能对于注解类描述不恰当,但是是为了表达对于该方法每次调用都是同一个注解类),那么在通过代理模式修改了注解类的字段值之后,那么对于该字段值影响是永久的。但是我错误的认为该注解类是原型的(即每次访问都算是一个全新的注解类)这就导致我业务代码书写错误了。