JAVA问题记录-利用Spring AOP来拦截特定方法上的注解并根据业务修改注解字段值
2018-03-03 23:01
543 查看
问题起因:
首先有块业务,需要添加埋点内容,但是此处埋点在入参值中有一个字段值,这个字段值的不同,会发送不同的打点信息(这个是在接口返回正确的情况下调用的),奔着解耦原业务和埋点的出发点,于是选择利用注解和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托管的,所以对于该类的默认对象实例是单例类,那么对于加在该类方法上的注解类也是单例的(可能对于注解类描述不恰当,但是是为了表达对于该方法每次调用都是同一个注解类),那么在通过代理模式修改了注解类的字段值之后,那么对于该字段值影响是永久的。但是我错误的认为该注解类是原型的(即每次访问都算是一个全新的注解类)这就导致我业务代码书写错误了。
首先有块业务,需要添加埋点内容,但是此处埋点在入参值中有一个字段值,这个字段值的不同,会发送不同的打点信息(这个是在接口返回正确的情况下调用的),奔着解耦原业务和埋点的出发点,于是选择利用注解和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托管的,所以对于该类的默认对象实例是单例类,那么对于加在该类方法上的注解类也是单例的(可能对于注解类描述不恰当,但是是为了表达对于该方法每次调用都是同一个注解类),那么在通过代理模式修改了注解类的字段值之后,那么对于该字段值影响是永久的。但是我错误的认为该注解类是原型的(即每次访问都算是一个全新的注解类)这就导致我业务代码书写错误了。
相关文章推荐
- 基于java.util.logging实现轻量级日志记录库(增加根据当前类class初始化,修复线程池模型(javaEE)下的堆栈轨迹顺序与当前调用方法不一致问题)
- 基于java.util.logging实现轻量级日志记录库(增加根据当前类class初始化,修复线程池模型(javaEE)下的堆栈轨迹顺序与当前调用方法不一致问题)
- 记录Java中方法同步问题
- 利用泛型抽取Dao层,加事务注解问题(java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType)
- Java反射:根据方法名动态调用方法,解决商品动态属性取值问题。
- 利用泛型抽取Dao层,加事务注解问题(java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType)
- java.lang.OutOfMemoryError: PermGen space的问题及修改JVM的内存大小方法
- Java反射高级应用--利用反射调用类的私有方法修改私有方法值,以及替换Java的类成员数据
- Java解决在浏览器地址栏中输入url访问action的问题以及拦截方法过滤的简易实现
- java中不利用get方法获得私有属性、不利用set方法修改私有属性、通过执行私有方法getName获得私有属性、通过执行私有方法setName更改私有属性name的值
- 利用多线程解决多业务不同定时区间歇触发问题的一种方法
- Spring 代理对象,cglib,jdk的问题思考,AOP 配置注解拦截 的一些问题.为什么不要注解在接口,以及抽象方法.
- 利用一下java的反射根据字符串找类,方法,然后执行方法
- 利用java技术生成验证码的多种方法及遇到问题的解决
- 【记录开发中遇到问题】修改xcode安装目录后终端找不到路径方法
- 利用sql2005的新特性实现根据子表条件得到的主表键且按其排序取出对应主表记录的方法
- 修改版的java导入编码问题(方法)
- 根据excel中的列表值,导入注解单元写入到指定包下所有java类方法中
- Java反射高级应用--利用反射调用类的私有方法修改私有方法值,以及替换Java的类成员数据
- spring aop 拦截业务方法,实现权限控制