Java做UI自动化和app自动化中动态代理@FindBy的工作原理【杭州多测师_王sir】【杭州多测师】...

news2024/11/26 14:24:02

Java做UI自动化和app自动化中动态代理@FindBy的工作原理一、背景简介
由于Selenium框架采用PageObject设计模式让测试代码与被测页面对象代码分离,因而提供了不少很方便的注解来达到目的,其中有一个注解就是@FindBy。在使用中,只要通过在field中使用注解,则可以将不同属性的元素对象转换成一个WebElement对象。

 通过WebElement提供的方法,则可以进行UI层面的操作了,下面来简单看看这个神奇的注解是怎么工作的。二、注解@FindBy的使用

@FindBy(name = "修改密码")
public WebElement changePswTab;

通过指定name属性,可以将changePswTab转换成当前页面的一个WebElement对象
代码示例如下:

三、注解定义
注解的定义很简单,直接看源码即可,通过注解定义可以知道,可以通过id,name,className,xpath等多种方式来锁定当前元素

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
public @interface FindBy {
  How how() default How.UNSET;

  String using() default "";

  String id() default "";

  String name() default "";

  String className() default "";

  String css() default "";

  String tagName() default "";

  String linkText() default "";

  String partialLinkText() default "";

  String xpath() default "";
}

四、注解行为注入
注解的行为肯定是在使用前注入的,那这个@FindBy具体做了什么呢?
1.将元素构建成By对象

  public By buildBy() {
    assertValidAnnotations();

    By ans = null;
    ...

    FindBy findBy = field.getAnnotation(FindBy.class);
    if (ans == null && findBy != null) {
      ans = buildByFromFindBy(findBy);
    }
    ...

    return ans;
  }

在Annotations中,selenium通过反射拿到findby对象,然后将对象构建成By对象

  protected By buildByFromShortFindBy(FindBy findBy) {
    if (!"".equals(findBy.className()))
      return By.className(findBy.className());
    ...

    if (!"".equals(findBy.name()))
      return By.name(findBy.name());
    ...

    // Fall through
    return null;
  }

  public static By name(final String name) {
    if (name == null)
      throw new IllegalArgumentException(
          "Cannot find elements when name text is null.");

    return new ByName(name);
  }

表面上,注解执行到这里就完成了,但是其实可以发现,在field注解使用时,拿到的是一个webelement对象!并不是现在拿到的by对象?这是怎么一回事呢?其实在这里,selenium使用了动态代理的方式来讲by对象转成webelement对象!五、动态代理转换实现
通过回溯findBy的调用方法,可以回溯到PageObject.initElements(...),但其实这个调用中,起作用的是这一段

  public Object decorate(ClassLoader loader, Field field) {
    if (!(WebElement.class.isAssignableFrom(field.getType())
          || isDecoratableList(field))) {
      return null;
    }

    ElementLocator locator = factory.createLocator(field);
    if (locator == null) {
      return null;
    }

    if (WebElement.class.isAssignableFrom(field.getType())) {
      return proxyForLocator(loader, locator);
    } else if (List.class.isAssignableFrom(field.getType())) {
      return proxyForListLocator(loader, locator);
    } else {
      return null;
    }
  }

selenium通过factory.createLocator(field)来实现by对象的构建,然后将by转换成
webelement则是proxyForLocator(loader, locator)来实现的。

  protected WebElement proxyForLocator(ClassLoader loader, ElementLocator locator) {
    InvocationHandler handler = new LocatingElementHandler(locator);

    WebElement proxy;
    proxy = (WebElement) Proxy.newProxyInstance(
        loader, new Class[]{WebElement.class, WrapsElement.class, Locatable.class}, handler);
    return proxy;
  }

动态代理中,真正调用的是InvocationHandler的实现对象. 当调用代理对象的接口时, 实际上会通过InvocationHandler.invkoe将调用转发给实际的对象,即new LocatingElementHandler(locator),所以只需要看看LocatingElementHandler的invoke方法做了啥就知道了。

public Object invoke(Object object, Method method, Object[] objects) throws Throwable {
    WebElement element;
    try {
      element = locator.findElement();
    } catch (NoSuchElementException e) {
      if ("toString".equals(method.getName())) {
        return "Proxy element for: " + locator.toString();
      }
      throw e;
    }

    if ("getWrappedElement".equals(method.getName())) {
      return element;
    }

    try {
      return method.invoke(element, objects);
    } catch (InvocationTargetException e) {
      // Unwrap the underlying exception
      throw e.getCause();
    }
  }

很明显,调用了findElement()方法,这样就实现了转换。六、为什么要用动态代理
因为在对一个页面进行测试时,涉及到很多元素,但真正执行可能只用到其中几个元素而已,使用动态代理的好处就是,不用在我们设计实现的时候就指定某一个代理类来代理哪一个被代理对象, 而把这种指定延迟到程序运行时由JVM来实现,相当于用例执行,元素调用时才去加载元素,简单来说就是实现了元素延迟加载。

七、关于动态代理的解释==》JDK获取动态代理对象
jdk获取动态代理类对象的步骤:

     1、创建原始对象----之后的功能方法以及类加载器会用到
        UserService service=new UserServiceImpl();
     2、 jdk创建动态代理对象
         Proxy.newProxyInstance(classLoder,interfaces,invocationHandler);

1)首先我们必须知道:Java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,还有一个Method类,通过使用类和接口可以生成JDK动态代理类或动态代理对象。 也就是:

1、Proxy提供用于创建动态代理类和代理对象的静态方法,也是所有动态代理类的父类。
 
    2、invocationHandler(接口):表示你的代理类要干什么,额外功能的编写,接口中就一个方法 invke():表示代理对象要执行的功能代码,我们的功能(额外功能+原始类功能)就写在这个方法中
      方法原型:public Object invoke(Object proxy, Method method, Object[] args){}
          参数:
              --object proxy:jdk创建的代理对象,无需复制
              --Method method:目标类中的方法
              --Object[] args:目标类中方法的参数
      3、Method
          作用:通过method.invoke(目标对象,方法的参数) 可以执行某个目标类方法

2)proxy提供了这样的一个静态方法来创建动态代理类对象

    static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandlerh)
       
       所以我们只需要使用这个方法就能获取jdk动态代理的对象
    1、ClassLoader loader : 类加载器
        作用:通过类加载器把对应类(目标类)的字节码文件加载到JVM中
        获取:每一个类的class文件,自动分配与之对应的类加载器
        但是:动态代理类没有对应的字节码文件,JVM也不会为它分配类加载器,所以我们需要借一个-----classLoder=UserService.class.getClassLoader();
    2、Class<?>[] interfaces:接口,目标对象实现的接口--也是通过反射获得的
            interfaces=service.getClass().getInterfaces();
    3、InvocationHandlerh:代理类需要完成的功能--这里通过匿名内部类实现
        @参数详解请上翻
         InvocationHandler invocationHandler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("前置增强的代码执行-----额外功能");
                Object returnValue = method.invoke(service, args); // 原始方法的执行
                return returnValue;
            }
        };
  
 我们现在已经实现了获取动态代理类对象方法的实参值
     编码步骤:
         1、创建原始类对象
         2、获取类加载器
         3、获取原始类实现的接口
         4、额外功能
         5、构建动态代理对象并赋予实参
UserService userService = (UserService)Proxy.newProxyInstance(classLoader, interfaces, invocationHandler); // 代理对象

八、关于@FindBy的工作原理、JDK获取动态代理对象、CGLIB获取动态代理对象和AOP参考了如下文章:

http://t.zoukankan.com/liujiarui-p-12408742.html

https://blog.csdn.net/qq_44774198/article/details/12601997

https://blog.csdn.net/m0_62884713/article/details/127085803

https://blog.csdn.net/akisi/article/details/126339951 

https://www.jianshu.com/p/cb8c3258839a 

https://zhuanlan.zhihu.com/p/39545755/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/90222.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ruoyi 在页面上增加一个显示字段(数据库增加字段,页面后端处理)

例如&#xff0c;上图所示&#xff0c; 【用户工资表】现有字段为用户id&#xff0c;用户工资&#xff0c;生效时间&#xff0c;备注信息 这些字段也就是你设计数据库时&#xff0c;数据库工资表字段 &#xff0c;现在&#xff0c;要让显示这个页面&#xff0c;增加一个用户姓…

Flutter for Web 首次首屏优化——JS 分片优化

作者&#xff1a;马坤乐(坤吾) Flutter for Web&#xff08;FFW&#xff09;从 2021 年发布至今&#xff0c;在国内外互联网公司已经得到较多的应用。作为 Flutter 技术在 Web 领域的有力扩充&#xff0c;FFW 可以让熟悉 Flutter 的客户端同学直接上手写 H5&#xff0c;复用 A…

2023年有哪些半入耳蓝牙耳机?半入耳式蓝牙耳机排行榜

工作生活中最常用的真无线蓝牙耳机来说&#xff0c;各式各样、价格悬殊的产品&#xff0c;很多人不知道该如何选择&#xff0c;半入耳式的佩戴舒适度一直都是公认的好&#xff0c;下面小编分享几个性能表现、续航时间都非常优秀的半入耳式蓝牙耳机 TOP1:南卡小音舱蓝牙耳机 音…

Java IO流补充(字符流)

字符 那么在Java中的字符用char来表示&#xff0c;char存储字符。Java使用Unicode来表示字符。Unicode可以表示在所有人类语言中找到的所有字符。Java char是16位类型 字符的范围是 0 ~ 65536 ,没有负字符。字符可以是文字、字母数字、符号等等。 字符流 尽管Java中字节流的…

【Spring】——17、@Resource注解和@Inject注解?

&#x1f4eb;作者简介&#xff1a;zhz小白 公众号&#xff1a;小白的Java进阶之路 专业技能&#xff1a; 1、Java基础&#xff0c;并精通多线程的开发&#xff0c;熟悉JVM原理 2、熟悉Java基础&#xff0c;并精通多线程的开发&#xff0c;熟悉JVM原理&#xff0c;具备⼀定的线…

使用 systemd 管理多个 MySQL 服务器实例

使用 systemd 管理多个 MySQL 服务器实例 文章目录使用 systemd 管理多个 MySQL 服务器实例先决条件支持 systemd 的操作系统每个实例配置独立的目录和参数部署多实例环境1. 配置选项文件使用 systemd 管理 MySQL 多实例总结本文仅讲述使用 RPM 包安装的多个 MySQL 实例如何使用…

IronXL 2022.12.10926 Crack

关于适用于 .NET 的 IronXL 在 C# 中阅读和编辑 Excel 电子表格&#xff0c;无需 MS Office 或 Excel Interop。 IronXL for .NET 允许开发人员在 .NET 应用程序和网站中读取、生成和编辑 Excel&#xff08;和其他电子表格文件&#xff09;。您可以读取和编辑 XLS/XLSX/CSV/TSV…

操作系统-内存管理(内存的分配与回收(非连续分配方式,基本分页存储管理方式,基本地址变换机构))

文章目录1. 基本分页存储管理基本地址变换机构1. 基本分页存储管理 分页存储&#xff1a; 将内存空间分为一个个大小相等的分区&#xff08;eg&#xff1a;每个分区4KB&#xff09;&#xff0c;每个分区就是一个页框 每个页框有一个编号&#xff0c;即页框号&#xff0c;页框…

C++ 重要笔记与题 (循环-嵌套-数组)

文章目录十 循环10.1 字符与循环10.2 数与循环十一 循环嵌套——不拘一格11.2数学与循环——脉脉相通十 循环 10.1 字符与循环 题1.1&#xff1a;循环输出26个字母&#xff0c;从A-Z。 for(char i A;i<Z;i){cout<<i<<" ";}题1.2&#xff1a;输入某…

第二证券|定增市场回暖 机构争抢优质项目

今年下半年以来&#xff0c;定增商场持续回暖。最新数据显现&#xff0c;到12月13日&#xff0c;下半年以来A股商场实施定增计划的上市公司近200家&#xff0c;比较上半年的133家显着增加。持续回暖的定增商场吸引了不少组织的目光&#xff0c;不只百亿级私募活跃捡拾筹码&…

大学生个人博客网页设计模板 学生个人博客网页成品 简单个人网站作品下载 静态HTML CSS个人网页作业源代码

&#x1f389;精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

[附源码]Python计算机毕业设计SSM基于java网上心理咨询系统数据分析(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

docke+gitlab+jenkins+springboot

安装Gitlab 开放防火墙端口80和配置映射文件夹 firewall-cmd --zonepublic --add-port80/tcp --permanent firewall-cmd --reload mkdir -p /docker_data/gitlab/{data,logs,config}启动Gitlab容器&#xff08;启动容器之前确保80&#xff0c;443端口没用被占用&#xff0c;被…

通话类型转换流程之AudioCall到VideoCall

目录 概述时序图关键代码关键log总结 一、概述 这里的通话类型指的是语音通话和视频通话&#xff0c;转换包括upgrade/ downgrade升降级&#xff0c;就是语音通话升级为视频通话、视频通话降级为语音通话。升级为视频通话一般就是包括如下图示的4步&#xff0c;MO发起请求&…

Microsoft SharePoint Online 更新功能可能是下一次勒索攻击的目标

Microsoft SharePoint Online是被使用最广泛的内容管理平台之一。但令人担忧的是&#xff0c;最近几年我们发现大部分攻击者可以滥用 SharePoint Online 和 OneDrive for Business 中的某项功能来加密您的所有文件并以此来勒索赎金。 SharePoint Online 据观察发现可能存在潜在…

四旋翼无人机学习第15节--PCB Editor简单绘制封装-手动绘制封装

文章目录1 前言2 class与sub class3 手动绘制3.1 芯片手册分析3.2 手动绘制1 前言 上一篇博客我们学习了获取封装的几种途径&#xff0c;分别是下载&#xff0c;软件生成与软件转化。本次博客开始讲手动绘制封装。 2 class与sub class 参考博客&#xff1a;第11讲、Allegro …

前端高频手写面试题集锦

手写深度比较isEqual 思路&#xff1a;深度比较两个对象&#xff0c;就是要深度比较对象的每一个元素。> 递归 递归退出条件&#xff1a; 被比较的是两个值类型变量&#xff0c;直接用“”判断被比较的两个变量之一为null&#xff0c;直接判断另一个元素是否也为null 提前结…

对受控组件和非受控组件的理解,以及应用场景?

一、受控组件 受控组件&#xff0c;简单来讲&#xff0c;就是受我们控制的组件&#xff0c;组件的状态全程响应外部数据 举个简单的例子&#xff1a; class TestComponent extends React.Component {constructor (props) {super(props);this.state { username: lindaidai }…

从事生活垃圾(含粪便)经营性清扫、收集、运输服务许可证

《城市生活垃圾管理办法》&#xff08;2007年4月28日建设部令第157号公布2015年5月4日修正本&#xff09;第十七条从事城市生活垃圾经营性清扫、收集、运输的企业&#xff0c;应当取得城市生活垃圾经营性清扫、收集、运输服务许可证。 未取得城市生活垃圾经营性清扫、收集、运输…