JAVA问题记录-利用Spring AOP来拦截特定方法上的注解并根据业务修改注解字段值

问题起因:

    首先有块业务,需要添加埋点内容,但是此处埋点在入参值中有一个字段值,这个字段值的不同,会发送不同的打点信息(这个是在接口返回正确的情况下调用的),奔着解耦原业务和埋点的出发点,于是选择利用注解和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 memberValues = (Map) 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 memberValues这么个字段中,所有才有sourceField = handler.getClass().getDeclaredField("memberValues");这么一句,在执行完这一句之后,我们会获取到Map memberValues字段,并且通过   memberValues.put("source", 7);这样类似的语句来实现控制注解的字段值内容。

到这里所有的业务实现都已经完成了,接下来说说遇到的坑点.....

===================================华丽的分界线================================

坑点描述:加在方法上的注释类,由于当前类是由Spring托管的,所以对于该类的默认对象实例是单例类,那么对于加在该类方法上的注解类也是单例的(可能对于注解类描述不恰当,但是是为了表达对于该方法每次调用都是同一个注解类),那么在通过代理模式修改了注解类的字段值之后,那么对于该字段值影响是永久的。但是我错误的认为该注解类是原型的(即每次访问都算是一个全新的注解类)这就导致我业务代码书写错误了。

你可能感兴趣的:(Spring,JavaWeb开发,Java开发记录)