+ 首页>>技能>>快速科普>>内容

技能[测试人也Code] JAVA反射机制-陶醉测者系列54次围观

背景

弱弱的郁闷了一回,前几天参加了BAT之一某公司的测试开发工程师的面试,整个过程感觉自己表现极差,第一个问题就问的JAVA的反射机制,上来就给我一个下马威,我平日里只是知道有这个概念,而且算是JAVA语言的一个大特点,但是苦于确实身为一个测试,虽然会接触到这样那样的框架,但是针对于使用JAVA反射机制的技巧始终没有这个主动性去学习,这次算是给自己敲响了警钟,还是上期中的老话,希望自动化测试工程师不仅仅是停留在使用工具上或者只是写一点小代码,当然写这篇博文除了给自己扫扫盲以外,更想表达一个问题-我花了周末半天的时间了解JAVA反射机制并写了一个JAVA的实例,真希望面试前我就写过这篇博文,然后让自己整个面试过程都显得相对良好,但是这并不代表我就是一个优秀的自动化测试工程师,我还是坚持一个观点,解决问题的能力才是工程师的核心能力,当然也许一些自认很牛的公司希望用一些比较学院派的东西去考察面试者来选拔出他们认为优秀的员工(以至于鄙人在面试过程中的测试设计环节问答上没有想多说的欲望,然后笑眯眯地说:对不起,你们这个产品我日常生活中几乎不用… …其实完全是出于对之前问题的不满),暂且不评论那么多了,那么下次我也多去了解一些所谓的“面经”,说不定人家还认为我也不错呢。

预备知识

OK,言归正传,首先我们先了解下什么是JAVA的反射机制,更多的概念性东西请前往百度百科,的确土了点: http://baike.baidu.com/view/1865203.htm?fr=aladdin

接下来我从这些繁杂的文字当中自己小小地总结一些要点:

  •  如何理解反射?

JAVA之所以引入一个reflect的概念,尤其强调的是reflect这个单词的象征性含义,简单说就是JAVA的程序在运行的过程中同样提供了一个自己的镜像一般的“影子”–计算机领域把这种特点称之为self-representation(自省或自表达),在这个过程中,开发人员从“影子”中便可获得JAVA实体运行程序的全部属性,并且允许开发再去修改这些属性。

  • 为什么JAVA会引入反射机制?

其实说到这个问题,不得不说JAVA引入这个机制的目的其实是为了顺应潮流,弥补自己的软肋,平时大家可能听说过静态语言与动态语言的划分问题,这又是一个很蛋疼的概念性问题,还是给个链接,感兴趣的话各位测友自己去翻阅看看:http://baike.baidu.com/view/1458275.htm  ,那么其实你会发现,JAVA(C#也一样)其实是介于这种静态与动态之间的语言,然而其实现在的系统或者框架以至于模块都趋向对外开放的可拓展性,这是动态语言的优势(测友的最爱python还有笔者曾经用过的ruby就是典型的动态语言),所以JAVA选择反射机制来让自己“显得”很像动态语言,请注意我这里的措辞。这种可能性的确是一种进步,但是反射机制不是一个性能很高的行为特点,因此要巧妙而冷静地去应用。

  • 我们利用JAVA反射都干些什么事情呢?

一般情况下,我们会利用这一个特性,获取一个JAVA Class下的全部属性信息(类信息,类变量,所有的方法信息,所有的注解信息等,并且我们还能修改他们),可能这样解释还不直观,说几个利用反射机制你能干的事情吧,比如你想列出来一个类下所有的方法,就如同你在一些IDE中能够看到一个Class下的树状结构一样,那么利用JAVA反射机制便可以实现这个功能;如果您使用过JDBC去和数据库打交道的话,不知道您是否还记得如何加载实例化DB驱动的环节(如:Class.forName(“com.mysql.jdbc.Driver.class”).newInstance();),其实我现在才知道,这就是一个典型的JAVA反射机制的应用,以后可以装下B了,呵呵;JAVA反射机制的经典应用还出现在著名的JAVA web类明星企业级应用开发框架组合SSH中,以Hibernate为例,该框架大量的使用了JAVA的反射机制来动态的加载并执行不同的函数,让JAVA的接口和抽象类的定义在实践中发挥到了极致,利用JAVA反射机制完成的基础代码架构的部分几乎不需要过多的修改便可以满足新增的需求(打断一下,这里说一个现实,其实JAVA的反射机制的确是在框架类代码中才被广泛使用,如果只是应用级别的人,比如几个做APP的小开发人员,我想他们也许只是知道这个东西,是否在应用我可以画一个大大的问号,作为测试人员的我们,能真正实践的可能更是少数),当然这也是我们测试框架一直追求的–高可复用性,高可拓展性,高可维护性,现实中其实我自己却很少应用,以后如果在做小的测试工具和所谓的测试框架时,我也要强迫自己应用这些特点,至少也能跟的上那些学院派大牛的步伐。

  • JAVA反射机制应用的一般设计步骤
  1. 先获取Class(可利用Class.forName;Object.class;Object.TYPE)
  2. 实例化Class对象(利用newInstance())
  3. 这时就厉害了,你将可以获得此类下的各种信息(包括:Constructor(构造函数),Method (所有已定义的方法),Field (所有类的属性,包括类中的变量))
  4. 接下来你便可以无法无天了
  • JAVA反射机制应用过程中的不安全因素

聪明的测友也许会感受到一个问题,那就是你会发现因为有了JAVA的反射机制,那些所谓的public,private可见性限制及作用域问题都变的形同虚设一样,当然sun公司不可能无视这些东西,因此伟大的sun公司创建了一个类AccessibleObject,提供了一个方法setAccessible来避免滥用反射带来的安全隐患。其实这个应用是很难的一件事情,非技术细节狂人还是不要去触碰为好,我只是轻轻地了解了一下这个东西(这几周由于测试执行性能方面的限制,我把一个单线程的测试工具的代码改为多线程都各种坑需要填,对于这个反射安全性的应用,鄙人还是飘过为妙)。

反射实践

如果上面的废话很多很讨厌的话,那么“show me your code”更务实一些,所有的实践和理解都写在这里。。。
接上次的工厂类设计模式,这里当是复习一下:

Demo Code:

  • 创建接口
public interface TestingListener {
//一个伪测试状态监视器的接口定义    
    public void startListener();    
    public void stopListener();    
    public int getTestingStatusCode(String status);
}
  • 实现接口

GUI层测试状态监视器

public class GuiFunctiontestingListener implements TestingListener{
//这是一个GUI层的QA验证程序的状态监听器
        boolean isStarted = false;
        boolean isEnded = false;
        @Override
        public void startListener() {
            // TODO Auto-generated method stub
            long startTime = System.currentTimeMillis();
            if(isStarted == false){
                System.out.println(">>>Start listener=>GuiFunctiontestingListener");
                isStarted = true;
                isEnded = false;
            }
            if(this.isStarted){
                try {
//假启动时间
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            long endTime = System.currentTimeMillis() - startTime;    
                System.out.println(">>>listener=>GuiFunctiontestingListener started!Time costing:"+endTime+" ms");
            }else{
                System.err.println(">>>listener=>GuiFunctiontestingListener starting meet trouble!");
            }
        }

        @Override
        public void stopListener() {
            // TODO Auto-generated method stub
            long startTime = System.currentTimeMillis();
            if(isEnded == false){
                System.out.println(">>>Stop listener=>GuiFunctiontestingListener");
                isStarted = false;
                isEnded = true;
            }
            if(this.isEnded){
                try {
//假关闭时间
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            long endTime = System.currentTimeMillis() - startTime;    
                System.out.println(">>>listener=>GuiFunctiontestingListener stopped!Time costing:"+endTime+" ms");
            }else{
                System.err.println(">>>listener=>GuiFunctiontestingListener ending meet trouble!");
            }
        }

        @Override
        public int getTestingStatusCode(String status) {
            // TODO Auto-generated method stub
            int statusCode = 0;
            switch(status){
            case "Success":
                statusCode = 200;
                break;
            case "Fail":
                statusCode = 300;
                break;
            case "Unknown":
                statusCode = 400;
                default:
                    break;
            }
            return statusCode;
        }
}

接口层测试状态监视器

public class InterfaceFunctiontestingListenner implements TestingListener{
//这是一个Interface层的QA验证程序的状态监听器
        boolean isStarted = false;
        boolean isEnded = false;
        @Override
        public void startListener() {
            // TODO Auto-generated method stub
            long startTime = System.currentTimeMillis();
            if(isStarted == false){
                System.out.println(">>>Start listener=>InterfaceFunctiontestingListenner");
                isStarted = true;
                isEnded = false;
            }
            if(this.isStarted){
                try {
//假启动时间
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            long endTime = System.currentTimeMillis() - startTime;    
                System.out.println(">>>listener=>InterfaceFunctiontestingListenner started!Time costing:"+endTime+" ms");
            }else{
                System.err.println(">>>listener=>InterfaceFunctiontestingListenner starting meet trouble!");
            }
        }

        @Override
        public void stopListener() {
            // TODO Auto-generated method stub
            long startTime = System.currentTimeMillis();
            if(isEnded == false){
                System.out.println(">>>Stop listener=>InterfaceFunctiontestingListenner");
                isStarted = false;
                isEnded = true;
            }
            if(this.isEnded){
                try {
//假关闭时间
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            long endTime = System.currentTimeMillis() - startTime;    
                System.out.println(">>>listener=>InterfaceFunctiontestingListenner stopped!Time costing:"+endTime+" ms");
            }else{
                System.err.println(">>>listener=>InterfaceFunctiontestingListenner ending meet trouble!");
            }
        }

        @Override
        public int getTestingStatusCode(String status) {
            // TODO Auto-generated method stub
            int statusCode = 0;
            switch(status){
            case "Success":
                statusCode = 200;
                break;
            case "Fail":
                statusCode = 300;
                break;
            case "Unknown":
                statusCode = 400;
                break;
                default:
                    break;
            }
            return statusCode;
        }
}

数据库数据验证测试状态监视器

public class DBlevelDataValidationListener implements TestingListener{
//这是一个DB层的QA验证程序的状态监听器
    boolean isStarted = false;
    boolean isEnded = false;
    @Override
    public void startListener() {
        // TODO Auto-generated method stub
        long startTime = System.currentTimeMillis();
        if(isStarted == false){
            System.out.println(">>>Start listener=>DBlevelDataValidationListener");
            isStarted = true;
            isEnded = false;
        }
        if(this.isStarted){
            try {
//假启动时间
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        long endTime = System.currentTimeMillis() - startTime;    
            System.out.println(">>>listener=>DBlevelDataValidationListener started!Time costing:"+endTime+" ms");
        }else{
            System.err.println(">>>listener=>DBlevelDataValidationListener starting meet trouble!");
        }
    }

    @Override
    public void stopListener() {
        // TODO Auto-generated method stub
        long startTime = System.currentTimeMillis();
        if(isEnded == false){
            System.out.println(">>>Stop listener=>DBlevelDataValidationListener");
            isStarted = false;
            isEnded = true;
        }
        if(this.isEnded){
            try {
//假关闭时间
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        long endTime = System.currentTimeMillis() - startTime;    
            System.out.println(">>>listener=>DBlevelDataValidationListener stopped!Time costing:"+endTime+" ms");
        }else{
            System.err.println(">>>listener=>DBlevelDataValidationListener ending meet trouble!");
        }
    }

    @Override
    public int getTestingStatusCode(String status) {
        // TODO Auto-generated method stub
        int statusCode = 0;
        switch(status){
        case "Success":
            statusCode = 200;
            break;
        case "Fail":
            statusCode = 300;
            break;
        case "Unknown":
            statusCode = 400;
            break;
            default:
                break;
        }
        return statusCode;
    }
}
  • 创建工厂类
public class FactoryClass {
  public static TestingListener instanceGUIListener(){
       return new GuiFunctiontestingListener();
  }

  public static TestingListener instanceInterfaceListener(){
       return new InterfaceFunctiontestingListener();
  }

  public static TestingListener instanceDBListener(){
       return new DBlevelDataValidationListener();
  }
}

以上步骤全部类似于之前设计模式中工厂模式的定义。

  • 下面写一个利用JAVA反射机制来获取工厂类的函数信息的测试类(重点)

Demo code

import java.lang.reflect.Method;
public class ShowFunctions {
//获取指定类的所有函数信息
String className;
//简单的JAVA POJO模式,这种Getter,Setter经常被应用在JAVA的代码中,用于对类变量进行变量的交换和传递
  private String getClassName() {
    return className;
  }
  private void setClassName(String className) {
    this.className = className;
  }

  ShowFunctions(String className){
    setClassName(className);
  try {
    Class<?> cluzz = Class.forName(getClassName());
    if(cluzz != null){
//此种方法会获取public及非public的任何已定义的函数
      Method methods[] = cluzz.getDeclaredMethods();
      System.out.println("[INFO]Totally got "+methods.length+" methods in CLASS named "+ getClassName());
      for(Method realMethod:methods){
        System.out.println("[INFO]Method info: "+realMethod.toString());
      }
  }
    } catch (ClassNotFoundException e) {
      e.printStackTrace();
    }
  }
}
  • 测试一下

Test Code

public class TestShowFunctions {
    public static void main(String[] args) {
        String className = "FactoryClass";
//获取类名为FactoryClass下定义的全部方法信息        
        new ShowFunctions(className);
    }
}
  • 运行结果

[INFO]Totally got 3 methods in CLASS named FactoryClass
[INFO]Method info: public static TestingListener FactoryClass.instanceGUIListener()
[INFO]Method info: public static TestingListener FactoryClass.instanceInterfaceListener()
[INFO]Method info: public static TestingListener FactoryClass.instanceDBListener()

  • 接下来通过JAVA反射机制实现一个动态执行测试Lisener的执行器,简单的一个例子,当然大多数网上的实例都是通过invoke()方法来实现的,这里没有这么实现,只是利用反射机制获取了方法名,再调用工厂模式的工厂类中实例化的对象,进而完成进一步的方法执行。

Demo Code
伪监听器执行器

import java.lang.reflect.Method;

public class ListenerExecutor {
  ListenerExecutor(String instanceName,String status){
  try {
    TestingListener listener = null;
    Class<?> cluzz = Class.forName("FactoryClass");
    try {
      Method executedMethod = cluzz.getDeclaredMethod(instanceName);
      String methodName = executedMethod.getName();
      if(listener == null){
        if(methodName.equals("instanceGUIListener")){
        listener = FactoryClass.instanceGUIListener();
        listener.startListener();
        int statusCode = listener.getTestingStatusCode(status);
        System.out.println("Status Code: "+statusCode);
        listener.stopListener();
    }
    if(methodName.equals("instanceInterfaceListener")){
      listener = FactoryClass.instanceInterfaceListener();
      listener.startListener();
      int statusCode = listener.getTestingStatusCode(status);
      System.out.println("Status Code: "+statusCode);
      listener.stopListener();
    }
    if(methodName.equals("instanceDBListener")){
      listener = FactoryClass.instanceInterfaceListener();
      listener.startListener();
      int statusCode = listener.getTestingStatusCode(status);
      System.out.println("Status Code: "+statusCode);
      listener.stopListener();
    }
  }
} catch (NoSuchMethodException e) {
   e.printStackTrace();
} catch (SecurityException e) {
   e.printStackTrace();
}} catch (ClassNotFoundException e) {
   e.printStackTrace();
 }
}
}
  • 测试一下

Demo Code

public class TestListenerExecutor {
private static String s_SUCCESS = "Success";
private static String s_FAIL = "Fail";
private static String s_UNKNOWN = "Unknown";

public static void main(String[] args) {
new ListenerExecutor("instanceGUIListener",s_SUCCESS);
new ListenerExecutor("instanceInterfaceListener",s_FAIL);
new ListenerExecutor("instanceDBListener",s_UNKNOWN);
}

}
  • 运行结果

>>>Start listener=>GuiFunctiontestingListener
>>>listener=>GuiFunctiontestingListener started!Time costing:2999 ms
Status Code: 200
>>>Stop listener=>GuiFunctiontestingListener
>>>listener=>GuiFunctiontestingListener stopped!Time costing:2003 ms
>>>Start listener=>InterfaceFunctiontestingListenner
>>>listener=>InterfaceFunctiontestingListenner started!Time costing:3001 ms
Status Code: 300
>>>Stop listener=>InterfaceFunctiontestingListenner
>>>listener=>InterfaceFunctiontestingListenner stopped!Time costing:2001 ms
>>>Start listener=>InterfaceFunctiontestingListenner
>>>listener=>InterfaceFunctiontestingListenner started!Time costing:3001 ms
Status Code: 400
>>>Stop listener=>InterfaceFunctiontestingListenner
>>>listener=>InterfaceFunctiontestingListenner stopped!Time costing:2001 ms

好了,今天就先给各位测友介绍到这里,例子的应用很简单,实现了一个假的测试状态监听器,使用一个独立的Executor去执行,实例化方法的选择完全通过JAVA的反射机制来动态获取方法名,这样做的好处大家应该能感受的到,那就是少了很多的HardCode而多了一些灵活与开放,实际上绝大多数的JAVA框架都或多或少的采取了JAVA的这个特点,来使得他们的框架的开放性更强,更容易被拓展,下次如果笔者还是在面试中被问到了这个话题,相信便不会捉襟见肘了,但是我最后还是不得不强调一下,知道这些东西也许是一个附加值,但绝对不是一个测试工程师的核心竞争力,事实上我们不需要去说一堆别人听不懂的东西来彰显自己的能力,能解决问题吗?这才是重点。

+ 猜你喜欢...

===== 关于 DiggerPlus =====
DiggerPlus是国内新锐测试人垂直内容博客,专注于挖掘测试工具,普及测试技术,专注于测试人能力提升,博客上线后就受到广大测试人的热烈追捧;此外,DiggerPlus深度整合评测资源,揭秘科技数据和真相,让读者更懂科技,更好地把玩科技。 我们始终保持"独立,客观,严谨,优秀"的优良作风,努力为读者带来源源不断的优质内容。想访问更多酷毙的测试人网站?赶紧来DiggerPlus测试人网址导航看看吧!

===== DiggerPlus Team =====
DiggerPlus Team是DiggerPlus官方发起的团队,这是一群热爱科技,热爱测试,喜欢深度挖掘的热血测试人,他们是评测师,测试专家。 我们欢迎优秀的测试人加入DiggerPlus Team。 加入DiggerPlus,可以成为我们的认证Dper,认证Dper拥有DiggerPlus独一无二的专栏并得到个人展示。

===== DiggerPlus 官方读者群 =====
DiggerPlus官方读者群(一)

+ 关于本文作者

Python/C/C++/Javascript程序员,持续学习者,目前专注于前端开发。

的专栏 | 专栏作者 | 访问小A的主页

+ 已有2个评论

开源中国精彩推送

基于开源中国OpenAPI开发
  • Copyright © 2014 DiggerPlus. 90 queries in 0.996 seconds.
    使用合作网站账号快速登录,更多精彩等着您: 开源中国