反射的应用

最近在公司搞一些llt的白盒测试用例,经常需要使用到反射进行实例的替换,将实际的类替换为伪装代理类来进行数据的打桩。这个时候会遇到很多需要访问私有属性的情况,甚至需要访问到final的属性,这个时候,需要进行一些特殊的处理。如果写的不对,欢迎各位在评论区进行提点,试验后,我会进行相应的修改。

先来个示例的bean类:

 1 package com.iwuyc.test.powermockito.powermockito.test;
 2 
 3 import java.util.concurrent.atomic.AtomicInteger;
 4 
 5 /**
 6  * Hello world!
 7  *
 8  */
 9 public class DemoBean
10 {
11     private static final AtomicInteger privateStaticFinalField = new AtomicInteger();
12     
13     private static AtomicInteger privateStaticField = new AtomicInteger();
14     
15     private AtomicInteger privateField = new AtomicInteger();
16 
17     public static AtomicInteger getPrivatestaticfinalfield()
18     {
19         return privateStaticFinalField;
20     }
21 
22     public static AtomicInteger getPrivateStaticField()
23     {
24         return privateStaticField;
25     }
26 
27     public AtomicInteger getPrivateField()
28     {
29         return privateField;
30     }
31 
32     
33 }

 

·访问private修饰的属性:

 1     @Test
 2     public void testGetPrivateField() throws Exception
 3     {
 4         DemoBean demoBean = new DemoBean();
 5         AtomicInteger valOld = demoBean.getPrivateField();
 6         
 7         Field privateField = DemoBean.class.getDeclaredField("privateField");
 8         //修改可访问的权限,对于私有的不可访问的,我们可以通过设置改值进行强制访问
 9         //该方法不仅仅是只有Field的实力中存在,在Method、Construct中也存在该方法
10         //也就是说,访问私有的Method跟私有的Construct也是可以进行强制访问的
11         privateField.setAccessible(true);
12         
13         privateField.set(demoBean, new AtomicInteger());
14         
15         AtomicInteger valNew = demoBean.getPrivateField();
16         
17         Assert.assertFalse("Two values is equals,but that was not my expected.",valOld.equals(valNew));
18     }

·访问private static修饰的属性

 1     @Test
 2     public void testGetPrivateStaticField() throws Exception
 3     {
 4         AtomicInteger valOld = DemoBean.getPrivateStaticField();
 5 
 6         Field privateField = DemoBean.class.getDeclaredField("privateStaticField");
 7         privateField.setAccessible(true);
 8 
 9         //该方法跟访问私有的属性是类似的,不同的只是,在调用Field实例的set方法的时候,可以不传实例
10         //当然传实例也是可以进行设置的
11         privateField.set(null, new AtomicInteger());
12 
13         AtomicInteger valNew = DemoBean.getPrivateStaticField();
14         Assert.assertFalse("Two values is equals,but that was not my expected.", valOld.equals(valNew));
15     }

·访问private static final修饰的属性

 1  @Test
 2     public void testGetPrivateStaticFinalField() throws Exception
 3     {
 4         AtomicInteger valOld = DemoBean.getPrivatestaticfinalfield();
 5 
 6         Field targetField = DemoBean.class.getDeclaredField("privateStaticFinalField");
 7         targetField.setAccessible(true);
 8 
 9         //其实访问final的属性,只需要将这个Field的一个标志位(modifiers)修改为非final的
10         Field modifiersField = Field.class.getDeclaredField("modifiers");
11         modifiersField.setAccessible(true);
12         //利用位运算,计算出非final的位,这题步是关键
13         modifiersField.set(targetField, targetField.getModifiers() & ~Modifier.FINAL);
14 
15         targetField.set(null, new AtomicInteger());
16         AtomicInteger valNew = DemoBean.getPrivatestaticfinalfield();
17         Assert.assertFalse("Two values is equals,but that was not my expected.", valOld.equals(valNew));
18     }

关于这一段代码,我稍微解释下吧。

我在研读Field实例的代码的时候,发现,其中存在一个modifiers的属性,这个属性记录着对应属性的修饰符,通过修改final的位,可以进行修改final这个修饰符,来达到强制替换属性实例的目的,这个修改会导致该属性变为非final的。

~~~~~~~~~~~~~2016-11-30 23:54:36 补充编辑~~~~~~~~~~~~~

今天在调用方法的时候遇到了参数列表中存在值类型的问题。

这个时候,应当使用使用下面的类型进行获取方法:

1 long.class <==> Long.TYPE
2 double.class <==> Double.TYPE
3 float.class <==> Float.TYPE
4 bool.class <==> Boolean.TYPE
5 char.class <==> Character.TYPE
6 byte.class <==> Byte.TYPE
7 void.class <==> Void.TYPE
8 short.class <==> Short.TYPE

上述表示各值类型对应的类类型,在查找方法的时候,需要将相应的值类型传入参数列表中。

范例如下:

public class DemoClass
{
    public void getMethod(int input)
    {
        System.out.println("int:" + input);
    }

    public void getMethod(Integer input)
    {
        System.out.println("Integer:" + input);
    }
}

调用两个方法的方式如下:

public class ReflectiveTest
{
    @Test
    public void testGetIntMethod() throws Exception
    {
        Method getMethod = DemoBean.class.getDeclaredMethod("getMethod", Integer.TYPE/*or use int.class*/);
        DemoBean demoBean = new DemoBean();
        getMethod.invoke(demoBean, 10);
    }
    
    @Test
    public void testGetIntegerMethod() throws Exception
    {
        Method getMethod = DemoBean.class.getDeclaredMethod("getMethod", Integer.class/*Different from int.class*/);
        DemoBean demoBean = new DemoBean();
        getMethod.invoke(demoBean, 10);
    }
}

分别输出:

int:10
Integer:10

 ~~~~~~~~~~~~~2016年12月6日 22:12:14 补充编辑~~~~~~~~~~~~~

示例代码:

 1 package com.iwuyc.test.reflective.tool;
 2 
 3 public class DemoBean
 4 {
 5 
 6     class InnerDemoBean
 7     {
 8         public InnerDemoBean()
 9         {
10 
11         }
12 
13         public InnerDemoBean(String name)
14         {
15         }
16     }
17 }

 

测试代码:

 1 package com.iwuyc.test.reflective.tool;
 2 
 3 import static org.junit.Assert.*;
 4 import java.lang.reflect.Constructor;
 5 
 6 import org.junit.Test;
 7 
 8 public class DemoBeanTest
 9 {
10     String innerDemoBeanName = DemoBean.class.getName() + "$InnerDemoBean";
11 
12     @Test
13     public void testWrong() throws Exception
14     {
15         // Wrong demo
16         Class<?> clazz = Class.forName(innerDemoBeanName);
17         Constructor<?> constructor = clazz.getDeclaredConstructor(String.class);
18         constructor.newInstance("hello");
19     }
20 
21     @Test
22     public void testValid() throws Exception
23     {
24         // Valid demo
25         DemoBean demoBeanInstance = new DemoBean();
26 
27         Class<?> clazz = Class.forName(innerDemoBeanName);
28         Constructor<?> constructor = clazz.getDeclaredConstructor(DemoBean.class, String.class);
29         Object innerDemoBeanInstance = constructor.newInstance(demoBeanInstance, "hello");
30         assertTrue(innerDemoBeanName.equals(innerDemoBeanInstance.getClass().getName()));
31     }
32 }

在方法testWrong()中,会报 java.lang.NoSuchMethodException 错误,出现这个异常的原因是参数列表的类型给错了,为什么会出现这样的问题呢?

其实问题出在 InnerDemoBean 中,因为在这个类的声明没有加上static,也就是说,这个类只会在DemoBean进行实例化的时候才会进行加载,并且,会在所有的构造函数中的参数列表中默认加上DemoBean的形参。

也就是说原来的 public InnerDemoBean()   public InnerDemoBean(String name) 两个方法,在反射中变成了 public InnerDemoBean(DemoBean demoBean)  public InnerDemoBean(DemoBean demoBean,String name) 。

此时,想要找到这两个构造函数,则需要想第28行那样,将DemoBean传入参数列表中以获取对应的构造函数,并且在使用该构造函数的时候,需要将DemoBean的一个实例传入参数列表中,以便参数构造函数使用。

posted @ 2016-11-30 00:54  iWuYc  阅读(344)  评论(0编辑  收藏  举报