Java에서 다른 클래스의 개인 필드 값을 읽는 방법은 무엇입니까?
서드파티의 설계가 서투른 클래스가 있습니다.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가 코멘트한 바와 같이 필드에 액세스하고 액세스 가능한 것으로 설정하며 값을 취득할 경우 모두 느려집니다.Exception
다만, 체크 마크가 붙어 있는 예외는 상기의 코멘트뿐입니다.
그NoSuchFieldException
선언된 필드에 해당하지 않는 이름으로 필드를 요청하면 이 메시지가 표시됩니다.
obj.getClass().getDeclaredField("misspelled"); //will throw NoSuchFieldException
그IllegalAccessException
필드에 액세스 할 수 없는 경우(예를 들어, 비공개이며, 이 필드가 누락되어 액세스 할 수 없는 경우)에 느려집니다.f.setAccessible(true)
선.
그RuntimeException
던질 수 있는s는 다음 중 하나입니다.SecurityException
s(JVM이SecurityManager
필드의 접근성을 변경할 수 없습니다.) 또는IllegalArgumentException
s: 필드 클래스 유형이 아닌 객체의 필드에 액세스하려고 할 경우
f.get("BOB"); //will throw IllegalArgumentException, as String is of the wrong type
apache commons-lang3에서 시도합니다.
FieldUtils.readField(object, fieldName, true);
문제를 해결하는 방법(클래스/컴포넌트의 개인 기능/동작에 액세스 하는 것)은 리플렉션뿐만이 아닙니다.
대체 솔루션은 .jar에서 클래스를 추출하여 Jode 또는 Jad를 사용하여 디컴파일하고 필드를 변경하여 원래 .jar에 대해 다시 컴파일하는 것입니다.그런 다음 새로운 .class를.jar
클래스 패스에 재삽입하거나.jar
. (jar 유틸리티를 사용하면 기존 .jar에 압축을 풀고 다시 삽입할 수 있습니다.)
아래에 기술한 바와 같이, 단순히 필드에 액세스/변경하는 것이 아니라 개인 상태에 액세스/변경하는 광범위한 문제를 해결합니다.
이를 위해서는.jar
물론 서명하지 않습니다.
아직 언급되지 않은 또 다른 옵션은 그루비를 사용하는 것입니다.Groovy를 사용하면 언어 설계의 부작용으로 개인 인스턴스 변수에 액세스할 수 있습니다.필드에 대한 getter가 있는지 여부에 관계없이
def obj = new IWasDesignedPoorly()
def hashTable = obj.getStuffIWant()
Java의 Reflection을 사용하면 모든 웹 사이트에 액세스할 수 있습니다.private/public
클래스 간에 필드 및 메서드를 지정합니다.단, 이 섹션의 Oracledocumentation에 따라 다음 사항을 권장합니다.
"반사는 코드가 개인 필드 및 메서드에 액세스하는 등 비반사 코드에서 불법적인 작업을 수행할 수 있도록 하기 때문에 반사를 사용하면 예기치 않은 부작용이 발생하여 코드가 기능하지 않게 되어 휴대성이 파괴될 수 있습니다.반사적인 코드는 추상화를 깨기 때문에 플랫폼의 업그레이드에 따라 동작이 바뀔 수 있습니다.」
다음은 Reflection의 기본 개념을 보여주는 코드 스냅입니다.
반사 1.자바
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");
}
}
반사 2.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에서 설명한 바와 같이 리플렉션 기능을 사용하여 접근 제한을 회피할 수 있습니다(Security Manager를 사용하면 허용된다고 가정).
하지만 이 수업이 너무 형편없어서 그런 해킹에 의지할 수 있다면 다른 방법을 찾아야 할 것 같습니다.이 작은 해킹으로 지금 몇 시간을 절약할 수 있겠지만 앞으로 비용이 얼마나 들까요?
바이트 코드를 직접 변경하려면 Sult Java Optimization 프레임워크를 사용합니다.http://www.sable.mcgill.ca/soot/
Sult는 완전히 Java로 작성되어 있으며 새로운 Java 버전에서 작동합니다.
스프링을 사용하는 경우:
테스트 컨텍스트에서 Reflection(반영)Test Utils는 최소한의 노력으로 도움이 되는 편리한 도구를 제공합니다."유닛 및 통합 테스트 시나리오에서 사용하기 위한 것"으로 기술되어 있습니다.
테스트 이외의 컨텍스트에서는 ReflectionUtils라는 이름의 유사한 클래스도 있지만, 이것은 "내부 사용만을 의도한 것"으로 기술되어 있습니다.이것이 의미하는 바를 올바르게 해석하려면 , 이 대답을 참조해 주세요.
원래의 투고에 기재되어 있는 예에 대처하려면 , 다음의 순서에 따릅니다.
Hashtable iWantThis = (Hashtable)ReflectionTestUtils.getField(obj, "stuffIWant");
다음 작업을 수행해야 합니다.
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);
}
거기에는 jOOR을 사용할 수 있습니다.
class Foo {
private final String value = "ABC";
}
class Bar {
private final Foo foo = new Foo();
public String value() {
return org.joor.Reflect
.on(this.foo)
.field("value")
.get();
}
}
class BarTest {
@Test
void accessPrivateField() {
Assertions.assertEquals(new Bar().value(), "ABC");
}
}
Java 9는 가변 핸들을 도입했습니다.이 필드를 사용하여 클래스의 개인 필드에 액세스할 수 있습니다.
예시의 코드는 다음과 같습니다.
var lookup = MethodHandles.lookup();
var handle = MethodHandles
.privateLookupIn(IWasDesignedPoorly.class, lookup)
.findVarHandle(IWasDesignedPoorly.class, "stuffIWant", Hashtable.class);
var value = handle.get(obj);
또한 다음을 사용하는 것이 좋습니다.Lookup
그리고.VarHandle
로서 이의를 제기하다.static final
[ ]를 클릭합니다.
반영에 대한 추가 참고 사항:일부 특별한 경우에서 같은 이름의 클래스가 여러 패키지에 있을 때 상위 답변에 사용된 반영이 개체에서 올바른 클래스를 선택하지 못할 수 있습니다.따라서 객체의 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);
(이것이 나에게 효과가 없었던 예시 수업입니다.)
다지관의 @JailBreak을 사용하여 직접적이고 안전한 Java 리플렉션을 수행할 수 있습니다.
@JailBreak Foo foo = new Foo();
foo.stuffIWant = "123;
public class Foo {
private String stuffIWant;
}
@JailBreak
잠금을 해제하다foo
컴파일러의 로컬 변수에서 모든 멤버에 직접 액세스 할 수 있습니다.Foo
의 계층입니다.
마찬가지로 jailbreak() 확장 메서드를 일회성으로 사용할 수 있습니다.
foo.jailbreak().stuffIWant = "123";
를 통해jailbreak()
에서 임의의 멤버에 액세스할 수 있는 방법Foo
의 계층입니다.
두 경우 모두 컴파일러는 필드 액세스를 마치 공개 필드처럼 안전하게 해결하는 반면, 매니폴드는 후드에서 사용자를 위해 효율적인 반사 코드를 생성합니다.
매니폴드에 대해 자세히 알아보십시오.
XrayInterface 도구를 사용하면 매우 간단합니다.누락된 getters/setters를 정의하기만 하면 됩니다.
interface BetterDesigned {
Hashtable getStuffIWant(); //is mapped by convention to stuffIWant
}
디자인 불량 프로젝트 X-ray:
IWasDesignedPoorly obj = new IWasDesignedPoorly();
BetterDesigned better = ...;
System.out.println(better.getStuffIWant());
내부적으로 이것은 반영에 의존한다.
데이터를 설정/취득할 수 있는 칼라스는 자신의 클래스 중 하나입니다.
작성만 하면 됩니다.public setter(Field f, Object value)
그리고.public Object getter(Field f)
그럴 수 있어요.회원 기능 내에서는 스스로 분리 체크도 할 수 있습니다.예: 세터의 경우:
class myClassName {
private String aString;
public set(Field field, Object value) {
// (A) do some checkings here for security
// (B) set the value
field.set(this, value);
}
}
물론, 이제 당신은 그 사실을 알아내야 한다.java.lang.reflect.Field
위해서sString
필드 값을 설정하기 전에.
이 기술은 일반적인 ResultSet-to-and-from-model-mapper에서 사용합니다.
언급URL : https://stackoverflow.com/questions/1196192/how-to-read-the-value-of-a-private-field-from-a-different-class-in-java
'programing' 카테고리의 다른 글
초기화되지 않은 포인터에 데이터를 복사/스캔/읽을 때 크래시 또는 "세그멘테이션 장애"가 발생합니다. (0) | 2022.07.16 |
---|---|
봄에 프로그래밍 방식으로 현재 활성/기본 환경 프로파일을 얻으려면 어떻게 해야 합니까? (0) | 2022.07.16 |
vue의 vue-good-table에 편집 버튼을 추가하는 방법 (0) | 2022.07.16 |
v- 탐색 모음에 반응하지 않는 경우 (0) | 2022.07.16 |
vuetify의 데이터 테이블에서 날짜 형식 변경 (0) | 2022.07.16 |