sitelink1 http://gracefulife.xyz/220627537434 
sitelink2  
sitelink3  
sitelink4  
sitelink5  
sitelink6  

자바를 이용해서 프로그래밍을 하다보면,

Reflection을 사용할일이 이리저리 많은데..

 

Reflection의 간략한 사용법을 정리해보고,

다음 포스팅에 전자정부 프레임워크의 EgovMap 을 (EgovMap 이나, 타 컬렉션을)

일반 모델 오브젝트로 변환하는 방법을 살펴보려 한다.

 

///////////////////////

 

Reflection은 Class 의 내부 정보를 조회하고 조작 할 수 있는 기법을 말한다. 

DI 라이브러리나, DI를 제공하는 프레임워크들은 아마도 

모두 리플렉션 기법을 이용해서 DI를 제공한다 라고 말할 수 있을 것이다.

(자바에만 있는 기법은 아니다. 런타임에서 클래스의 메타정보를 갖도록 컴파일을 하는 언어들은 갖고있는 기능)

하튼 이 리플렉션은 프로그래머에게 보다 유연하게 프로그래밍을 할 수 있는 방법을 제공한다.

 

하지만 리플렉션을 사용할 시 정적 타임체킹이 되지 않으므로, 조심해서 사용하여야 한다.

테스트를 하자 ! 

성능문제도 약간 있으나, 대부분의 제품에서 유의미한 정도의 차이를 발생시키지 않으므로.. 넘어간다.

 

 

아래의 내용은 링크에 있는 내용을 수행, 간단한 설명을 붙인것에 불과하니, 영어가 익숙한 사람은 이 링크를 참조하는 것이 좋을 듯 하다.

https://docs.oracle.com/javase/tutorial/reflect/class/classNew.html

 

리플렉션으로 할 수 있는 몇가지 간단한 작업을 정리해본다.

 

 

1. 클래스 정보 가져오기

 

리플렉션을 이용하기 위해 선행되어야 하는 작업이다.

리플렉션은 기본적으로 인스턴스의 메타 정보를 갖고(Class 정보) 이런저런 조작 기능을 제공하기 때문에,

인스턴스에서 인스턴스의 클래스 정보를 가져오는 기능이 필요한 경우가 많은데,

클래스 정보를 가져오기 위해선 다음과 같이 할 수 있다

public class Main {

    public static void main(String[] args) {
        Class c = "foo".getClass();
        System.out.println(c);
    }
}

위의 코드의 실행 결과를 예상해보자.

 

결과는 다음과 같다.

 

리플렉션_1.png

 

 

이 방법뿐만 아닌 여러 방식으로 Class 정보를 가져올 수 있으나, 대부분 

인스턴스.getClass 메소드나, 클래스.class 방식으로 class 정보를 가져온다.

 

 

2. 필드 조작 

 

 

필드 리스트를 가져올 시 

getDeclaredFields, getFields 두 메소드를 이용해서 가져올 수 있다.

다만 DeclaredFields 메소드는 나의 슈퍼클래스의 멤버는 가져오지 않으며, 

getFileds 는 private 멤버를 가져오지 않는다고 한다.

https://docs.oracle.com/javase/tutorial/reflect/class/classMembers.html

 

일반적으로 리플렉션을 사용해서 필드를 룩업할때는 대부분 getDeclaredField 메소드를 사용하는 것 같다.

일례로, Spring에서 제공하는 ReflectionUtils 클래스의 findField 메소드에서는 

다음과 같은 방식으로 필드를 가져오는 것을 확인할 수 있다. ( 스프링 3.2 코드 )

    public static Field findField(Class<?> clazz, String name, Class<?> type) {
//        Assert.notNull(clazz, "Class must not be null");
//        Assert.isTrue(name != null || type != null, "Either name or type of the field must be specified");
        Class<?> searchType = clazz;
        while (!Object.class.equals(searchType) && searchType != null) {
            Field[] fields = searchType.getDeclaredFields();
            for (Field field : fields) {
                if ((name == null || name.equals(field.getName())) && (type == null || type.equals(field.getType()))) {
                    return field;
                }
            }
            searchType = searchType.getSuperclass();
        }
        return null;
    }

이 메소드를 이용해서 간단한 예제를 만들어보면 

 

리플렉션_2.png

 

 

이러한 클래스의 testMember를 아래와 같은 방법으로 가져올 수 있다.

 

public static void main(String[] args) {
    Field field = findField(Test.class, "testMember", String.class);
    System.out.println(field);
}

출력시 결과 값은 다음과 같이 나오게된다.

 

리플렉션3.png

 

 

이렇게 필드 정보를 가져왔다면 실제 인스턴스의 값을 가져오기 위해서 

field.get(인스턴스) 방식으로 인스턴스의 내부 값을 가져올 수 있다.

 

이는 접근 지정자의 제약을 무시하고 런타임에서 인스턴스의 값을 가져오거나 값을 넣을 수 있다는 것을 의미한다. (조심해서 쓰자..)

 

 

3. 메소드 조작

 

 

리플렉션을 이용한다면 필드조작 뿐만 아니고 메소드를 조작할 수 있다.

메소드 또한 필드와 같이 getMethods, getDeclaredMethods 메소드를 사용하여 정보를 가져올 수 있다.

 

이 또한 스프링에 있는 ReflectionUtils 의 findMethod 를 참고해보자면 

    public static Method findMethod(Class<?> clazz, String name) {
//        Assert.notNull(clazz, "Class must not be null");
//        Assert.notNull(name, "Method name must not be null");
        Class<?> searchType = clazz;
        while (searchType != null) {
            Method[] methods = (searchType.isInterface() ? searchType.getMethods() : searchType.getDeclaredMethods());
            for (Method method : methods) {
                return method;
            }
            searchType = searchType.getSuperclass();
        }
        return null;
    }

(이 메소드는 원본과 약간 다르게 수정되었다. 스프링 내부적으로 리플렉션 캐시를 이용하는 부분이 있는데 이를 제외한 코드이다)

이런 식으로 사용될 수 있는데, searchType이 인터페이스일떈 getMethods를 통해서 메소드리스트를 가져오고, 

아닐때는 getDeclaredMethods를 가져오는 함수이다. 

인터페이스는 접근지정자를 사용하지 않고, 인터페이스간 extends가 가능하니 getMethods를 통해서 메소드리스트를 가져온 것이고,

클래스인 경우 일반적으로 사용하는 getDeclaredMethods를 통해서 메소드를 가져오는 방식이다.

 

이 방식으로 메소드를 가져와서 테스트를 해보면

public static void main(String[] args) {
    Field field = findField(Test.class, "testMember", String.class);
    Method method = findMethod(Test.class, "getTestMember");
    System.out.println(field);
    System.out.println(method);
}

아래와 같이 출력되는 것을 확인할 수 있다.

 

getMethod.png

 

 

 

이렇게 가져온 Method 객체는 invoke 메소드를 통해 실제로 수행할 수 있다.

 

 

4. 객체 생성하기

 

 

리플렉션을 이용하면, 생성자를 직접 호출하지 않고도 객체를 생성할 수 있다.

이를 이용해서 클래스 정보만 갖고도 인스턴스를 만들 수 있으며, 여러 방식으로 활용될 수 있다.

 

아까의 Test 클래스를 이용해서 실제 인스턴스를 만들고, 내부 멤버 필드에 값을 세팅해서 출력해보자.

 

public static void main(String[] args) {
    try {
        Test testInstance = Test.class.newInstance();
        Field field = findField(testInstance.getClass(), "testMember", String.class);
        if (field != null) {
            field.setAccessible(true);
            field.set(testInstance, "오세훈 멍청이..!");
            System.out.println(testInstance);
        }
    } catch (InstantiationException | IllegalAccessException e) {
        e.printStackTrace();
    }
}

 

field.setAccessible(true) 코드는 private field 에 직접 접근을 하기 위해서 호출하는 메소드이다. 

이를 제외한 나머지 코드는 전부 위에서 소개되었을 테니.. 생략한다.

다음의 코드를 수행해보면 다음과 같은 결과를 얻을 수 있다.

 

리플렉션4.png

 

 

위와 같은 간단한 방법으로.. 인스턴스화를 할 수 있으며 내부 필드 조작 및 메소드 실행까지 가능하다.

 

 

 

나는 개인적으로 싱글턴 패턴을 매우 싫어하는데.. 싱글턴을 이방식으로 깨트리는 방법을 소개하고.. 글을 마친다.

(이글에서는 이에 안전한 싱글턴 생성 방법 또한 소개하고 있으나....(Enum 방식. 이펙티브 자바에서 소개 된) 내취향은 아니야..)

https://stackoverflow.com/questions/20421920/what-are-the-different-ways-we-can-break-a-singleton-pattern-in-java

 

 

 

번호 제목 글쓴이 날짜 조회 수
251 Calendar, Date, Format, java.time 패키지 황제낙엽 2017.10.31 108
250 날짜, 시간 문자열 값으로 Date 오브젝트로 만들기 >> SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy", Locale.US) 황제낙엽 2017.10.31 1516
249 시스템 속성(System Property) 클래스를 이용하여 jni 라이브러리 사용하기 황제낙엽 2017.09.22 37
248 Java 실행 옵션들 황제낙엽 2017.08.23 3367
247 HttpsURLConnection 을 사용한 SSL서버 접속 file 황제낙엽 2017.08.02 231
246 서버구동시 주기적으로 동작을 수행하는 스레드를 함께 실행하는 서블릿 황제낙엽 2017.08.02 131
245 HttpURLConnection 사용 샘플( JSP , SERVLET ) 황제낙엽 2017.08.01 254
244 HttpURLConnection 사용하기 황제낙엽 2017.08.01 393
243 [HttpURLConnection] POST로 파라미터 넘기기 황제낙엽 2017.08.01 507
242 HttpURLConnection POST 방식 사용하기 황제낙엽 2017.08.01 370
241 Runtime 클래스를 이용한 윈도우 프로그램 실행 예제 황제낙엽 2017.08.01 113
240 JSON Util (JSON 을 다루기 위해 직접 작성한 유틸 클래스) file 황제낙엽 2017.07.10 461
» 자바 리플렉션(Java Reflection) 간단한 설명 및 사용방법 정리 file 황제낙엽 2017.07.10 135
238 Generate random numbers (Random.java) 황제낙엽 2017.07.02 490
237 쓰레드(Thread)를 중간에 종료시키는 방법 황제낙엽 2017.03.15 5127
236 JSON 라이브러리(API) 종류 황제낙엽 2017.01.18 404
235 [JSON기초04] 자바 JSON 데이터에서 KEY 값 알아오기 (TIP) 황제낙엽 2017.01.18 6641
234 [JSON기초03] 자바 JSON Google Simple JSON을 이용한 간단한 JSON DATA 파싱 황제낙엽 2017.01.18 566
233 [JSON기초02] 자바 JSON Google Simple JSON을 이용한 간단한 JSON DATA 생성 황제낙엽 2017.01.18 111
232 [JSON기초01] JSON이란? XML이란? JSON 개념, XML 개념 설명 황제낙엽 2017.01.18 408