Programming

Java의 다른 클래스에서 개인 필드의 값을 읽는 방법은 무엇입니까?

procodes 2020. 2. 15. 23:36
반응형

Java의 다른 클래스에서 개인 필드의 값을 읽는 방법은 무엇입니까?


제 3 자에 제대로 설계되지 않은 수업 JAR이 있으며 개인 필드 중 하나에 액세스해야 합니다. 예를 들어 왜 개인 필드를 선택해야합니까?

class IWasDesignedPoorly {
    private Hashtable stuffIWant;
}

IWasDesignedPoorly obj = ...;

리플렉션을 사용하여 값을 얻으려면 어떻게 stuffIWant해야합니까?


비공개 필드에 액세스하려면 클래스의 선언 된 필드 에서 가져 와서 액세스 할 수 있어야합니다.

Field f = obj.getClass().getDeclaredField("stuffIWant"); //NoSuchFieldException
f.setAccessible(true);
Hashtable iWantThis = (Hashtable) f.get(obj); //IllegalAccessException

편집 : aperkins 가 댓글을 달았 듯이 필드에 액세스하고 액세스 가능으로 설정하고 값을 검색하면 모두 throw됩니다 Exception. 단지 확인 해야 할 예외 사항은 위에 설명되어 있습니다.

NoSuchFieldException당신이 선언 된 필드에 해당하지 않은 이름으로 필드에 대한 요구한다면 던져 질 것이다.

obj.getClass().getDeclaredField("misspelled"); //will throw NoSuchFieldException

IllegalAccessException분야에 액세스 할 수없는 있다면 비공개이며, 놓치고를 통해 액세스 가능하지 않은 경우 (예를 들어, 던져 질 것이다 f.setAccessible(true)선을.

RuntimeException발생 될 수 있습니다들 중 하나입니다 SecurityException(JVM의이 경우의 SecurityManager당신이 필드의 접근성을 변경할 수 없습니다), 또는 IllegalArgumentException당신이하지 필드의 클래스의 유형의 개체에 액세스에게 필드를 시도하고있는 경우, S :

f.get("BOB"); //will throw IllegalArgumentException, as String is of the wrong type

FieldUtilsApache Commons-lang3에서 시도하십시오 .

FieldUtils.readField(object, fieldName, true);

리플렉션 만이 문제를 해결할 수있는 유일한 방법은 아닙니다 (클래스 / 컴포넌트의 개인 기능 / 행동에 액세스하는 것임)

다른 해결책은 .jar에서 클래스를 추출하고 Jode 또는 Jad를 사용하여 디 컴파일 하고 필드를 변경하거나 액세서를 추가 한 다음 원래 .jar에 대해 다시 컴파일하는 것입니다. 그런 다음 새 .class를 .jar클래스 경로 앞에 놓거나에 다시 삽입하십시오 .jar. jar 유틸리티를 사용하면 기존 .jar을 추출하고 다시 삽입 할 수 있습니다.

아래에 언급 된 것처럼, 이는 단순히 필드에 액세스 / 변경하는 것이 아니라 개인 상태에 액세스 / 변경하는 더 넓은 문제를 해결합니다.

이것은 필요 .jar하지 물론, 서명 할 수 있습니다.


아직 언급되지 않은 다른 옵션 : Groovy 사용하십시오 . Groovy를 사용하면 언어 디자인의 부작용으로 프라이빗 인스턴스 변수에 액세스 할 수 있습니다. 현장에 게터가 있든 없든 상관없이

def obj = new IWasDesignedPoorly()
def hashTable = obj.getStuffIWant()

Java 에서 Reflection을 사용하면 private/public한 클래스의 모든 필드와 메소드를 다른 클래스에 액세스 할 수 있지만 섹션 단점Oracle 문서 에 따라 다음과 같은 권장 사항이 있습니다.

"리플렉션을 사용하면 개인 필드 및 메서드에 액세스하는 것과 같이 비 반사 코드에서 불법적 인 작업을 코드에서 수행 할 수 있으므로 리플렉션을 사용하면 예기치 않은 부작용이 발생하여 코드가 제대로 작동하지 않아 이식성이 떨어질 수 있습니다. "추상화를 중단하고 플랫폼 업그레이드로 동작이 변경 될 수 있습니다."

다음은 리플렉션의 기본 개념을 보여주는 코드 스냅 샷입니다.

Reflection1.java

public class Reflection1{

    private int i = 10;

    public void methoda()
    {

        System.out.println("method1");
    }
    public void methodb()
    {

        System.out.println("method2");
    }
    public void methodc()
    {

        System.out.println("method3");
    }

}

Reflection2.java

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;


public class Reflection2{

    public static void main(String ar[]) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
    {
        Method[] mthd = Reflection1.class.getMethods(); // for axis the methods 

        Field[] fld = Reflection1.class.getDeclaredFields();  // for axis the fields  

        // Loop for get all the methods in class
        for(Method mthd1:mthd)
        {

            System.out.println("method :"+mthd1.getName());
            System.out.println("parametes :"+mthd1.getReturnType());
        }

        // Loop for get all the Field in class
        for(Field fld1:fld)
        {
            fld1.setAccessible(true);
            System.out.println("field :"+fld1.getName());
            System.out.println("type :"+fld1.getType());
            System.out.println("value :"+fld1.getInt(new Reflaction1()));
        }
    }

}

그것이 도움이되기를 바랍니다.


oxbow_lakes가 언급했듯이 리플렉션을 사용하여 액세스 제한을 피할 수 있습니다 (SecurityManager에서 허용한다고 가정).

즉,이 클래스가 너무 나쁘게 설계되어 해커에게 의지하게되면 대안을 찾아야 할 것입니다. 이 작은 해킹으로 인해 몇 시간이 절약 될 수 있지만 비용이 얼마나 들까 요?


Soot Java Optimization 프레임 워크를 사용하여 바이트 코드를 직접 수정하십시오. http://www.sable.mcgill.ca/soot/

그을음은 Java로 완전히 작성되었으며 새로운 Java 버전에서 작동합니다.


다음을 수행해야합니다.

private static Field getField(Class<?> cls, String fieldName) {
    for (Class<?> c = cls; c != null; c = c.getSuperclass()) {
        try {
            final Field field = c.getDeclaredField(fieldName);
            field.setAccessible(true);
            return field;
        } catch (final NoSuchFieldException e) {
            // Try parent
        } catch (Exception e) {
            throw new IllegalArgumentException(
                    "Cannot access field " + cls.getName() + "." + fieldName, e);
        }
    }
    throw new IllegalArgumentException(
            "Cannot find field " + cls.getName() + "." + fieldName);
}

리플렉션에 대한 추가 참고 사항 : 일부 특수한 경우, 동일한 이름을 가진 여러 클래스가 다른 패키지에 존재할 때 최상위 답변에 사용 된 리플렉션이 객체에서 올바른 클래스를 선택하지 못할 수 있습니다. 따라서 객체의 package.class가 무엇인지 알고 있다면 다음과 같이 전용 필드 값에 액세스하는 것이 좋습니다.

org.deeplearning4j.nn.layers.BaseOutputLayer ll = (org.deeplearning4j.nn.layers.BaseOutputLayer) model.getLayer(0);
Field f = Class.forName("org.deeplearning4j.nn.layers.BaseOutputLayer").getDeclaredField("solver");
f.setAccessible(true);
Solver s = (Solver) f.get(ll);

(이것은 저에게 효과적이지 않은 예시 수업입니다)


Spring을 사용하는 경우 ReflectionTestUtils 는 최소한의 노력으로 여기에 도움이되는 편리한 도구를 제공합니다. "단위 및 통합 테스트 시나리오에서 사용하기 위해" 설명되어 있습니다 . ReflectionUtils 라는 유사한 클래스도 있지만 "내부 용으로 만 사용" 으로 설명되어 있습니다. 이것이 의미하는 바에 대한 해석 이 답변참조하십시오 .

게시 된 예제를 해결하려면 다음을 수행하십시오.

Hashtable iWantThis = (Hashtable)ReflectionTestUtils.getField(obj, "stuffIWant");

직접적이고 유형이 안전한 Java 리플렉션을 위해 Manifold의 @JailBreak사용할 수 있습니다 .

@JailBreak Foo foo = new Foo();
foo.stuffIWant = "123;

public class Foo {
    private String stuffIWant;
}

@JailBreak의 계층 구조에 foo있는 모든 멤버에 직접 액세스 할 수 있도록 컴파일러에서 로컬 변수를 잠금 해제합니다 Foo.

마찬가지로 jailbreak () 확장 메소드를 일회용으로 사용할 수 있습니다.

foo.jailbreak().stuffIWant = "123";

jailbreak()방법을 통해 Foo의 계층 구조에 있는 모든 멤버에 액세스 할 수 있습니다 .

두 경우 모두, 컴파일러는 공개 필드처럼 사용자에게 안전하게 필드 액세스를 해결하는 반면 매니 폴드는 효율적인 리플렉션 코드를 생성합니다.

매니 폴드 에 대해 자세히 알아보십시오 .

참고 URL : https://stackoverflow.com/questions/1196192/how-to-read-the-value-of-a-private-field-from-a-different-class-in-java

반응형