Java

Reflection 사용1

체리필터 2021. 6. 25. 13:30
728x90
반응형

2021.06.25 - [Java] - Reflection 에서 Reflection 이란 무엇인지, 그리고 왜 사용하는지, 어디에서 사용 되는지 등을 확인해 봤다.

이번 포스트에서는 실제로 Reflection을 직접 사용해 보고 어떻게 동작하는지 확인해 보자.

먼저 테스트를 위한 Class를 작성하자.

package com.example.demo.reflection;

public class A {
    private String ps1 = "aaa";
    private String ps2;
    public String ps3;

    private A() {
        System.out.println("this is empty arg constructor");
    }

    private A(String ps2) {
        this.ps2 = ps2;
    }

    public A(String ps1, String ps2) {
        this.ps1 = ps1;
        this.ps2 = ps2;
    }

    private void method1(String arg1, Integer arg2) {
        System.out.println("this is mehtod1");
    }

    private void method2() {
        System.out.println("this is mehtod2");
    }

    public void method3() {
        System.out.println("this is method3");
    }
}

 

String 2개를 받아 들이는 생성자 외에는 모두 private 으로 만들어서 외부에서 접근할 수 없도록 만들자.

A Class를 이용하여 Reflection이 어떻게 동작하는지 살펴보자.

Class

정의된 class를 가져오는 방법은 다음과 같다.

package com.example.demo.reflection;

import org.junit.Test;

public class ReflectionTest {
    @Test
    public void getClassTest() throws ClassNotFoundException {
        Class clazz = A.class;
        System.out.println("class name is : " + clazz.getName());

        Class clazz2 = Class.forName("com.example.demo.reflection.A");
        System.out.println("class2 name is : " + clazz2.getName());
    }
}

java.lang.Class 형식으로 받기 위해서는 A.class 또는 Class.forName 을 사용할 수 있다.

forName 안에 인자로 들어가는 class 이름은 package 명까지 완벽하게 포함하여 입력해야 한다. 그렇지 않을 경우 ClassNotFoundException 을 발생 시키게 된다.

getClassTest 를 실행 시켜 보면 아래와 같은 결과가 나오게 된다.

class name is : com.example.demo.reflection.A
class2 name is : com.example.demo.reflection.A

 

Constructor

생성자를 가져오기 위해서는 아래와 같이 처리한다.

package com.example.demo.reflection;

import org.junit.Test;

import java.lang.reflect.Constructor;

public class ReflectionTest {
    @Test
    public void getConstructor() throws ClassNotFoundException, NoSuchMethodException {
        Class clazz = Class.forName("com.example.demo.reflection.A");
        Constructor constructor1 = clazz.getDeclaredConstructor();
        System.out.println("constructor1 is : " + constructor1);
        System.out.println("=============================");

        Constructor constructor2 = clazz.getDeclaredConstructor(String.class);
        System.out.println("constructor2 is : " + constructor2);
        System.out.println("=============================");

        Constructor constructors[] = clazz.getDeclaredConstructors();
        for (Constructor constructor: constructors) {
            System.out.println("constructors element is : " + constructor);
        }
        System.out.println("=============================");

        Constructor constructors2[] = clazz.getConstructors();
        for (Constructor constructor: constructors2) {
            System.out.println("constructors element is : " + constructor);
        }
    }
}

 

getDeclaredConstructor()와 같이 인자를 넣지 않게 된다면 인자 없는 생성자를 가져온다.

getDeclaredConstructor(String.class)와 같이 String을 인자로 넣게 된다면 A Class의 생성자 중 String을 인자로 받는 생성자를 가져온다.

getDeclaredConstructors를 하게 되면 정의 된 생성자를 모두 가져 온다. foreach 문을 돌면서 확인해 보면 정의 된 생성자가 모두 보여지는 것을 알 수 있다.

getDeclaredConstructors 대신에 getConstructors를 사용하게 되면 public으로 정의 된 생성자만 반환하게 된다.

다음은 getConstructor 테스트 메소드를 실행한 결과이다.

constructor1 is : private com.example.demo.reflection.A()
=============================
constructor2 is : private com.example.demo.reflection.A(java.lang.String)
=============================
constructors element is : public com.example.demo.reflection.A(java.lang.String,java.lang.String)
constructors element is : private com.example.demo.reflection.A(java.lang.String)
constructors element is : private com.example.demo.reflection.A()
=============================
constructors element is : public com.example.demo.reflection.A(java.lang.String,java.lang.String)
반응형

Method

메소드는 다음과 같이 찾을 수 있다.

    @Test
    public void methodTest() throws ClassNotFoundException, NoSuchMethodException {
        Class clazz = Class.forName("com.example.demo.reflection.A");
        Method method1 = clazz.getDeclaredMethod("method1", null);
        System.out.println("method is : " + method1);
    }

A Class의 method1의 경우 파라미터가 없기 때문에 이름으로는 method1을 넘기고 파라미터로는 null을 넘기거나 아예 첫 번째 파라미터만 넘겨도 된다.

만일 method1을 String을 받는 형태로 바꾼 후 위의 테스트 코드를 실행하게 된다면 아래와 같은 NoSuchMethodException이 떨어지게 된다.

java.lang.NoSuchMethodException: com.example.demo.reflection.A.method1()

	at java.lang.Class.getDeclaredMethod(Class.java:2130)
	at com.example.demo.reflection.ReflectionTest.methodTest(ReflectionTest.java:44)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:220)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:53)

이럴 경우에는 다음과 같이 바꾸면 된다.

Method method1 = clazz.getDeclaredMethod("method1", String.class);

만일 2개 이상의 파라미터가 필요하다면 다음과 같이 하면 된다.

    private void method1(String arg1, Integer arg2) {
        System.out.println("this is mehtod1");
    }

    @Test
    public void methodTest() throws ClassNotFoundException, NoSuchMethodException {
        Class clazz = Class.forName("com.example.demo.reflection.A");

        Class param[] = new Class[2];
        param[0] = String.class;
        param[1] = Integer.class;

        Method method1 = clazz.getDeclaredMethod("method1", param);
        System.out.println("method is : " + method1);
    }

method1이 String arg와 Integer arg를 받게 되어 있으므로 Class 배열을 생성 한 다음 넘기면 된다.

전체 메소드를 보고자 할 경우에는 getDeclaredMethods를 사용하면 된다.

    @Test
    public void methodTest() throws ClassNotFoundException, NoSuchMethodException {
        Class clazz = Class.forName("com.example.demo.reflection.A");

        Class param[] = new Class[2];
        param[0] = String.class;
        param[1] = Integer.class;

        Method method1 = clazz.getDeclaredMethod("method1", param);
        System.out.println("method is : " + method1);
        System.out.println("=============================");

        Method methods[] = clazz.getDeclaredMethods();
        for (Method method: methods) {
            System.out.println("method is : " + method);
        }
    }

위와 같이 작성 한 후 실행 하게 되면 아래와 같이 출력되게 된다.

method is : private void com.example.demo.reflection.A.method1(java.lang.String,java.lang.Integer)
=============================
method is : private void com.example.demo.reflection.A.method1(java.lang.String,java.lang.Integer)
method is : private void com.example.demo.reflection.A.method2()

 

public method만 찾을 경우에는 getMethod를 사용하면 된다. A class에 public method를 다음과 같이 추가하고 테스트 해보면 알 수 있다.

    public void method3() {
        System.out.println("this is method3");
    }

    @Test
    public void methodTest() throws ClassNotFoundException, NoSuchMethodException {
        Class clazz = Class.forName("com.example.demo.reflection.A");

        Method method3 = clazz.getMethod("method3");
        System.out.println("method3 is : " + method3);
    }

결과는 다음과 같다.

method3 is : public void com.example.demo.reflection.A.method3()

 

getMethods는 public method의 목록을 보여주며, 상속받은 모든 method들까지 보여 준다. 아래 내용을 통해 확인 할 수 있다.

    @Test
    public void methodTest() throws ClassNotFoundException, NoSuchMethodException {
        Class clazz = Class.forName("com.example.demo.reflection.A");

        Method methods2[] = clazz.getMethods();
        for (Method method:methods2) {
            System.out.println("method is : " + method);
        }
    }

method is : public void com.example.demo.reflection.A.method3()
method is : public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
method is : public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
method is : public final void java.lang.Object.wait() throws java.lang.InterruptedException
method is : public boolean java.lang.Object.equals(java.lang.Object)
method is : public java.lang.String java.lang.Object.toString()
method is : public native int java.lang.Object.hashCode()
method is : public final native java.lang.Class java.lang.Object.getClass()
method is : public final native void java.lang.Object.notify()
method is : public final native void java.lang.Object.notifyAll()

 

public method인 method3를 볼 수 있으며 그와 관련하여 상속받은 다른 메소드들까지 보여지게 된다.

 

Field

멤버 변수를 가져오기 위해서는 Field를 사용하면 된다. 테스트 코드는 아래와 같이 작성하였다.

    @Test
    public void getFieldsTest() throws ClassNotFoundException, NoSuchFieldException {
        Class clazz = Class.forName("com.example.demo.reflection.A");
        Field field1 = clazz.getDeclaredField("ps1");
        System.out.println("field1 is : " + field1);
        System.out.println("=============================");

        Field fields1[] = clazz.getDeclaredFields();
        for (Field field: fields1) {
            System.out.println("field is : " + field);
        }
        System.out.println("=============================");

        Field fields2[] = clazz.getFields();
        for (Field field: fields2) {
            System.out.println("field is : " + field);
        }
    }

getDeclaredField에 필드명을 넣으면 해당 이름을 가진 멤버 변수만을 가져오게 된다.

getDeclaredFields를 호출하게 되면 전체 멤버 변수를 가져 오게 된다.

getFields를 사용하면 public으로 생성 된 멤버 변수만 가져 오게 된다.

따라서 위의 테스트 코드를 실행하면 아래와 같이 나오게 된다.

field1 is : private java.lang.String com.example.demo.reflection.A.ps1
=============================
field is : private java.lang.String com.example.demo.reflection.A.ps1
field is : private java.lang.String com.example.demo.reflection.A.ps2
field is : public java.lang.String com.example.demo.reflection.A.ps3
=============================
field is : public java.lang.String com.example.demo.reflection.A.ps3

 

 

 

 

 

 

728x90
반응형