访问与反射非可见类(Accessing non-visible classes with refle

2019-07-20 08:00发布

我试图让一个不可见的类的实例,AKA包专用类,使用反射。 我在想,如果有切换修饰符把它公开,然后使用访问它的方式Class.forName 。 当我尝试,现在它停止我说我不能这样做。 不幸的是没有setAccesible的方法Class类。

Answer 1:

嵌套类 -其他类中定义的类(包括静态和非静态类)
内部类 -非静态嵌套类(内部类的实例需要外部类的实例存在)

非嵌套(顶层)的类

根据您的问题,我们知道你要访问的构造是不公开的。 所以,你的类可能看起来像这样( A类是在一些封装比我们不同)

package package1;

public class A {
    A(){
        System.out.println("this is non-public constructor");
    }
}

为了创建这个类,我们需要得到我们想要调用构造函数的实例,并使其可以访问。 当它完成,我们可以使用Constructor#newInstance(arguments)创建实例。

Class<?> c = Class.forName("package1.A");
//full package name --------^^^^^^^^^^
//or simpler without Class.forName:
//Class<package1.A> c = package1.A.class;

//In our case we need to use
Constructor<?> constructor = c.getDeclaredConstructor();
//note: getConstructor() can return only public constructors
//so we needed to search for any Declared constructor

//now we need to make this constructor accessible
constructor.setAccessible(true);//ABRACADABRA!

Object o = constructor.newInstance();

嵌套和内部类

如果你想访问嵌套的(静态和非静态)与类Class.forName你需要使用的语法:

Class<?> clazz = Class.forName("package1.Outer$Nested");

Outer$Nested说, Nested类中声明Outer类。 嵌套类是非常相似的方法,他们可以访问其外部类的所有成员(包括私人)。

但是,我们需要记住存在要求其外部类的实例内部类的该实例。 通常情况下,我们通过创建它们:

Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();

所以你看内部类的每个实例有关于它的外部类(参考该外实例存储在一些信息this$0场,更多的信息: 这是什么意思,如果一个变量的名称为“此$ 0”中的IntelliJ IDEA而调试Java? )

因此,在创建的实例Inner与类Constructor#newInstance()你需要传递的第一个参数参考的实例Outer类(模拟outer.new Inner()行为)。

下面是一个例子。

在包1

package package1;

public class Outer {
    class Inner{
        Inner(){
            System.out.println("non-public constructor of inner class");
        }
    }
}

在包装2

package package2;

import package1.Outer;
import java.lang.reflect.Constructor;

public class Test {
    public static void main(String[] args) throws Exception {

        Outer outerObject = new Outer();

        Class<?> innerClazz = Class.forName("package1.Outer$Inner");

        // constructor of inner class as first argument need instance of
        // Outer class, so we need to select such constructor
        Constructor<?> constructor = innerClazz.getDeclaredConstructor(Outer.class);

        //we need to make constructor accessible 
        constructor.setAccessible(true);

        //and pass instance of Outer class as first argument
        Object o = constructor.newInstance(outerObject);

        System.out.println("we created object of class: "+o.getClass().getName());

    }
}

静态嵌套类

静态嵌套类的实例不需要外部类的实例(因为它们是静态的)。 因此,在他们的情况下,我们并不需要寻找构造与Outer.class作为第一个参数。 而且我们也不需要通过外部类的实例作为第一个参数。 换句话说代码将是相同非嵌套(顶层)类(也许除了事实,你将需要添加$Nested语法Class.forName()



Answer 2:

Class.forName应该工作。 如果类是在一个包层次列表中的"package.access"安全属性,那么你将需要与适当的权限进行操作(通常是所有权限;或者没有安全管理器)。

如果你要使用Class.newInstance ,不要。 Class.newInstance不好处理异常。 相反,获得了Constructor和调用newInstance上。 这是很难看到你有什么用没有异常跟踪问题。

与以往一样,大多数但不是反射的所有用途是坏主意。



Answer 3:

我们最近发布了一个库,帮助了很多通过反射来访问私有字段,方法和内部类: BoundBox

对于像一个类

public class Outer {
    private static class Inner {
        private int foo() {return 2;}
    }
}

它提供了一个语法,如:

Outer outer = new Outer();
Object inner = BoundBoxOfOuter.boundBox_new_Inner();
new BoundBoxOfOuter.BoundBoxOfInner(inner).foo();

你所要做的创建BoundBox类的唯一的事情就是写@BoundBox(boundClass=Outer.class)BoundBoxOfOuter类将立即产生。



Answer 4:

我不得不要求从旧版本的对象复制字段的值,如果这个值是最新版本空。 我们有这两个选项。

核心Java:

for (Field f : object.getClass().getSuperclass().getDeclaredFields()) {
    f.setAccessible(true);
  System.out.println(f.getName());
  if (f.get(object) == null){
    f.set(object, f.get(oldObject));
  }
}

使用Spring [org.springframework.beans.BeanWrapper]:

BeanWrapper bw = new BeanWrapperImpl(object);
PropertyDescriptor[] data = bw.getPropertyDescriptors();
for (PropertyDescriptor propertyDescriptor : data) {
  System.out.println(propertyDescriptor.getName());
  Object propertyValue = bw.getPropertyValue(propertyDescriptor.getName());
  if(propertyValue == null )
    bw.setPropertyValue( new PropertyValue(propertyDescriptor.getName(),"newValue"));
}


Answer 5:

您可以使用歧管的 @Jailbreak直接,类型安全的Java反射:

Foo foo = new @Jailbreak Foo();

public class Foo {
    Foo() {...}

    private void yodog() {...}
}

这里@Jailbreak使编译器来解决构造类型安全,就好像公众,而歧管的发动机罩下为您生成有效的反射代码。

此外,您可以使用@Jailbreak访问和构建非可视类:

com.abc. @Jailbreak Bar bar = new com.abc. @Jailbreak Bar();

package com.abc;
// package-private class
class Bar {
    Bar() {...}
}

对于隐藏的类访问,Java的语法标注要求将其从包装标注的分开的类。

更一般地,你可以使用@Jailbreak任何类型的反思:

@Jailbreak Foo foo = new Foo();
foo.yodog();

@Jailbreak解锁在编译器中直接访问所有成员foo的局部变量Foo的层次结构。

同样,您可以使用一次性使用的越狱()扩展方法:

foo.jailbreak().yodog();

通过jailbreak()方法,你可以访问任何成员Foo的层次结构。

了解更多有关歧管 。



文章来源: Accessing non-visible classes with reflection