ROP扫描服务方法因桥方法产生的BUG的解决

Wesley13
• 阅读 439

ROP的服务类通过@ServiceMethodBean进行注解,服务方法通过@ServiceMethod标注,ROP在启动时自动扫描Spring容器中的Bean,将服务方法写到服务注册表中.

最近发现了一个问题,是由于Java泛型的桥方法和合成方法引起的,下面举例说明:

Java代码  ROP扫描服务方法因桥方法产生的BUG的解决

  1. package com.rop.session;

  2. /**

  3. * 其中T是登录请求的类,而R是注销请求的类

  4. * @author : chenxh

  5. * @date: 13-10-16

  6. */

  7. import com.rop.RopRequest;

  8. import org.slf4j.Logger;

  9. import org.slf4j.LoggerFactory;

  10. import java.util.UUID;

  11. public abstract class AuthenticationService<T extends RopRequest,R extends RopRequest> {

  12. ...

  13. public abstract  Object logon(T logonRequest);

  14. /**

  15. * 该方法在子类在实现,并打上@ServiceMethod注解,作为注销的服务方法

  16. * @param loginRequest

  17. * @return

  18. */

  19. public abstract Object logout(R logoutRequest);

  20. }

AuthenticationService定义了两个抽象方法,需要子类实现,以便实现登录认证.

子类实现如下:

Java代码  ROP扫描服务方法因桥方法产生的BUG的解决

  1. @ServiceMethodBean

  2. public class AppAuthenticationService extends AuthenticationService<LogonRequest,LogoutRequest> {

  3. public static final String USER_LOGON = "user.logon";

  4. public static final String USER_LOGOUT = "user.logout";

  5. ...

  6. @ServiceMethod(method = USER_LOGON, version = "1.0",

  7. needInSession = NeedInSessionType.NO,ignoreSign = IgnoreSignType.YES)

  8. @Override

  9. public Object logon(LogonRequest logonRequest) {

  10. ...

  11. }

  12. @ServiceMethod(method = USER_LOGOUT, version = "1.0")

  13. @Override

  14. public Object logout(LogoutRequest logoutRequest) {

  15. ...

  16. }

  17. }

AppAuthenticationService类中覆盖了抽象父类中的方法,并且对泛型进行了具化.

但是当ROP扫描服务方法时,服务方法的入参识别发生了错误,错将入参识别为RopRequest,而非
LogonRequest,LogoutRequest.

断点跟踪到注册服务方法时,发现AuthenticationService类居然有2个logon和2个logout方法:

ROP扫描服务方法因桥方法产生的BUG的解决

1.logon(LogonRequest r)
2.logout(LogoutRequest r)

3.logon(RopRequest r)
4.logout(RopRequest r)

其中前两个方法是AuthenticationService中定义的方法,而后两个方法是为了实现泛型具化JAVA自动生产的方法,称为桥方法,可参见这篇文章的说明:
http://jiangshuiy.iteye.com/blog/1339105

后两个方法也有和前两个方法一样的@ServiceMethod注解,因此在ROP扫描时,就可以扫描到桥方法,而把真正的方法覆盖了.

JAVA的Method反射类中拥有判断是否是桥方法的方法:

Java代码  ROP扫描服务方法因桥方法产生的BUG的解决

  1. Method#isBridge()

前两个方法返回的是false,而后两个方法返回的是true.

另外,桥方法也是合成方法(Synthetic),Method反射类中拥有判断是否是桥方法的方法:

Java代码  ROP扫描服务方法因桥方法产生的BUG的解决

  1. Method#isSynthetic()

关于合成方法,亦请参考http://jiangshuiy.iteye.com/blog/1339105

为了避免ROP扫描到这些杂攻杂八的方法,因此ROP扫描程序做了以下的调整:

Java代码  ROP扫描服务方法因桥方法产生的BUG的解决

  1. private void registerFromContext(final ApplicationContext context) throws BeansException {

  2. if (logger.isDebugEnabled()) {

  3. logger.debug("对Spring上下文中的Bean进行扫描,查找ROP服务方法: " + context);

  4. }

  5. String[] beanNames = context.getBeanNamesForType(Object.class);

  6. for (final String beanName : beanNames) {

  7. Class<?> handlerType = context.getType(beanName);

  8. //1只对标注 ServiceMethodBean的Bean进行扫描

  9. if(AnnotationUtils.findAnnotation(handlerType,ServiceMethodBean.class) != null){

  10. ReflectionUtils.doWithMethods(handlerType, new ReflectionUtils.MethodCallback() {

  11. public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {

  12. ReflectionUtils.makeAccessible(method);

  13. ...                            }

  14. },

  15. new ReflectionUtils.MethodFilter() {

  16. public boolean matches(Method method) {

  17. //2不是合成方法,且标注了ServiceMethod的方法!!

  18. return !method.isSynthetic() && AnnotationUtils.findAnnotation(method, ServiceMethod.class) != null;

  19. }

  20. }

  21. );

  22. }

  23. }

  24. ...

  25. }

点赞
收藏
评论区
推荐文章
Wesley13 Wesley13
2年前
java向python ,text文件动态传参或传值问题完美解决
由于业务需要对python文件进行参数传递,通过下面两个java方法完美解决此问题,我的思路是:首先我要先把上次写的参数删除,第二我要新的参数写到python文件中。第一个方法解决了删除上次传递的参数问题。第二个方法解决了参数传递到python文件/@paramfilepython文件的路径
待兔 待兔
3年前
Java中遍历HashMap的5种方式
本教程将为你展示Java中HashMap的几种典型遍历方式。如果你使用Java8,由于该版本JDK支持lambda表达式,可以采用第5种方式来遍历。如果你想使用泛型,可以参考方法3。如果你使用旧版JDK不支持泛型可以参考方法4。1、通过ForEach循环进行遍历importjava.io.IOException;importjav
Wesley13 Wesley13
2年前
java泛型
一、实现机制java泛型实现方法为类型擦除,基于这种方法实现的泛型称为伪泛型。java泛型只在源代码中存在,在编译后的文件中替换为原生类型,并插入强制转换。(真正的泛型是应该存在于源码、编译后文件、运行期)二、擦除实例源码:List<StringtestListnewArrayList<String();
Wesley13 Wesley13
2年前
JAVA Spring RMI(1)
在Spring整合Rmi中: 服务端使用了org.springframework.remoting.rmi.RmiServiceExporter RmiServiceExporter把任何Spring管理的Bean输出成一个RMI服务。通过把Bean包装在一个适配器类中工作。适配器类被绑定到RMI注册表中,并且将请求代理给服务类。 客户端使
Wesley13 Wesley13
2年前
Java日期时间API系列31
  时间戳是指格林威治时间1970年01月01日00时00分00秒起至现在的总毫秒数,是所有时间的基础,其他时间可以通过时间戳转换得到。Java中本来已经有相关获取时间戳的方法,Java8后增加新的类Instant等专用于处理时间戳问题。 1获取时间戳的方法和性能对比1.1获取时间戳方法Java8以前
Wesley13 Wesley13
2年前
Java日期时间API系列30
  实际使用中,经常需要使用不同精确度的Date,比如保留到天2020042300:00:00,保留到小时,保留到分钟,保留到秒等,常见的方法是通过格式化到指定精确度(比如:yyyyMMdd),然后再解析为Date。Java8中可以用更多的方法来实现这个需求,下面使用三种方法:使用Format方法、 使用Of方法和使用With方法,性能对比,使用
Stella981 Stella981
2年前
CentOS下宝塔如何部署Django项目?
基础环境装好宝塔服务宝塔里装好【Python项目管理器】宝塔里装好【Nginx】把Django项目代码发到服务器把代码放到服务器上有两种方法:方法一:服务器上安装Git,通过GitClone代码到服务器上方法二:通过宝塔的FTP工具把代码上传上去
Stella981 Stella981
2年前
Google地球出现“无法连接到登录服务器(错误代码:c00a0194)”解决方法
Google地球出现“无法连接到登录服务器(错误代码:c00a0194)”解决方法参考文章:(1)Google地球出现“无法连接到登录服务器(错误代码:c00a0194)”解决方法(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fwww.codeprj.com%2Fblo
Stella981 Stella981
2年前
SpringBoot2.x版本整合Redis进行数据缓存
项目放在github:在缓存开发中,有两个重要的接口:在这里面:  @Cacheable:  如果用这个注解标注在方法上,那么方法的结果就会被缓存存起来,这个多用于在查询的时候进行使用    比如: publicusergetuser(Integerid) 这个方法用这个注解标注的话,通过id查到的内容就会杯存在缓存中进行保存
Easter79 Easter79
2年前
SpringBoot2.x版本整合Redis进行数据缓存
项目放在github:在缓存开发中,有两个重要的接口:在这里面:  @Cacheable:  如果用这个注解标注在方法上,那么方法的结果就会被缓存存起来,这个多用于在查询的时候进行使用    比如: publicusergetuser(Integerid) 这个方法用这个注解标注的话,通过id查到的内容就会杯存在缓存中进行保存