您的位置:首页 > 其它

[01][01][04][01] setAccessible方法

wuqi13289 2020-10-16 21:51 99 查看 https://blog.51cto.com/1496245

1. 背景

在测试枚举类型实现单例模式能抵抗反射机制破解的代码中,看constructor.setAccessible(true)代码,不明白其用途

2. setAccessible方法详解

setAccessible并不是在Field中的,而是在AccessibleObject中,AccessibleObject类是Field(字段),Method(方法),Constructor(构造器)类的基类,它提供反射对象绕过Java语言权限控制检查的权限

public static void setAccessible(AccessibleObject[] array, boolean flag)
throws SecurityException {
SecurityManager sm = System.getSecurityManager();
if (sm != null) sm.checkPermission(ACCESS_PERMISSION);
for (int i = 0; i < array.length; i++) {
setAccessible0(array[i], flag);
}
}

public void setAccessible(boolean flag) throws SecurityException {
SecurityManager sm = System.getSecurityManager();
if (sm != null) sm.checkPermission(ACCESS_PERMISSION);
setAccessible0(this, flag);
}

private static void setAccessible0(AccessibleObject obj, boolean flag)
throws SecurityException
{
if (obj instanceof Constructor && flag == true) {
Constructor<?> c = (Constructor<?>)obj;
if (c.getDeclaringClass() == Class.class) {
throw new SecurityException("Cannot make a java.lang.Class" +
" constructor accessible");
}
}
obj.override = flag;
}

setAccessible(AccessibleObject[] array, boolean flag)
第一个参数array ,就是要设置flag标志位的Field/Method/Constructor对象的数组,第二个参数flag,将array数组的所有Field/Method/Constructor对象设置为flag标识符的新值

3. setAccessible方法使用

在测试了中方别对私有构造方法,私有方法,私有字段设置不检验权限

  • 访问权限类

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
    private Long id;
    private String name;
    private Integer age;
    private Double score;
    private String testMethod(){
    return "abcd";
    }
    }
  • 测试类

    public class SetAccessibleTest {
    public static void main(String[] args) {
    try {
    Class<?> clazz = User.class;
    Constructor constructor = clazz.getDeclaredConstructor(null);
    // 私有构造方法设置不检查权限
    constructor.setAccessible(true);
    User user = (User) constructor.newInstance();
    // 所有私有属性的方法设置不检查权限
    Method[] methods = clazz.getDeclaredMethods();
    AccessibleObject.setAccessible(methods, true);
    // 所有私有属性的字段设置不检查权限
    Field[] fields = clazz.getDeclaredFields();
    AccessibleObject.setAccessible(fields, true);
    fields[0].setLong(user,1L);
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    }

由于JDK的安全检查耗时较多,所以通过setAccessible(true)的方式关闭安全检查就可以达到提升反射速度的效果

标签: